366 lines
14 KiB
Java
366 lines
14 KiB
Java
/*
|
|
* Copyright (C) 2007 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package android.database;
|
|
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.annotation.SystemApi;
|
|
import android.annotation.UserIdInt;
|
|
import android.app.compat.CompatChanges;
|
|
import android.compat.annotation.ChangeId;
|
|
import android.compat.annotation.EnabledAfter;
|
|
import android.compat.annotation.UnsupportedAppUsage;
|
|
import android.content.ContentResolver.NotifyFlags;
|
|
import android.net.Uri;
|
|
import android.os.Handler;
|
|
import android.os.UserHandle;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.concurrent.Executor;
|
|
|
|
/**
|
|
* Receives call backs for changes to content.
|
|
* Must be implemented by objects which are added to a {@link ContentObservable}.
|
|
*/
|
|
@android.ravenwood.annotation.RavenwoodKeepWholeClass
|
|
public abstract class ContentObserver {
|
|
/**
|
|
* Starting in {@link android.os.Build.VERSION_CODES#R}, there is a new
|
|
* public API overload {@link #onChange(boolean, Uri, int)} that delivers a
|
|
* {@code int flags} argument.
|
|
* <p>
|
|
* Some apps may be relying on a previous hidden API that delivered a
|
|
* {@code int userId} argument, and this change is used to control delivery
|
|
* of the new {@code int flags} argument in its place.
|
|
*/
|
|
@ChangeId
|
|
@EnabledAfter(targetSdkVersion=android.os.Build.VERSION_CODES.Q)
|
|
private static final long ADD_CONTENT_OBSERVER_FLAGS = 150939131L;
|
|
private final Object mLock = new Object();
|
|
private Transport mTransport; // guarded by mLock
|
|
|
|
Handler mHandler;
|
|
private final Executor mExecutor;
|
|
|
|
/**
|
|
* Creates a content observer.
|
|
*
|
|
* @param handler The handler to run {@link #onChange} on, or null if none.
|
|
*/
|
|
public ContentObserver(Handler handler) {
|
|
mHandler = handler;
|
|
mExecutor = null;
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
* Creates a content observer with an executor.
|
|
*
|
|
* @param executor The executor to run {@link #onChange} on, or null if none.
|
|
* @param unused a second argument to avoid source incompatibility.
|
|
*/
|
|
public ContentObserver(@Nullable Executor executor, int unused) {
|
|
mExecutor = executor;
|
|
}
|
|
|
|
/**
|
|
* Gets access to the binder transport object. Not for public consumption.
|
|
*
|
|
* {@hide}
|
|
*/
|
|
public IContentObserver getContentObserver() {
|
|
synchronized (mLock) {
|
|
if (mTransport == null) {
|
|
mTransport = new Transport(this);
|
|
}
|
|
return mTransport;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets access to the binder transport object, and unlinks the transport object
|
|
* from the ContentObserver. Not for public consumption.
|
|
*
|
|
* {@hide}
|
|
*/
|
|
@UnsupportedAppUsage
|
|
public IContentObserver releaseContentObserver() {
|
|
synchronized (mLock) {
|
|
final Transport oldTransport = mTransport;
|
|
if (oldTransport != null) {
|
|
oldTransport.releaseContentObserver();
|
|
mTransport = null;
|
|
}
|
|
return oldTransport;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if this observer is interested receiving self-change notifications.
|
|
*
|
|
* Subclasses should override this method to indicate whether the observer
|
|
* is interested in receiving notifications for changes that it made to the
|
|
* content itself.
|
|
*
|
|
* @return True if self-change notifications should be delivered to the observer.
|
|
*/
|
|
public boolean deliverSelfNotifications() {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* This method is called when a content change occurs.
|
|
* <p>
|
|
* Subclasses should override this method to handle content changes.
|
|
* </p>
|
|
*
|
|
* @param selfChange True if this is a self-change notification.
|
|
*/
|
|
public void onChange(boolean selfChange) {
|
|
// Do nothing. Subclass should override.
|
|
}
|
|
|
|
/**
|
|
* This method is called when a content change occurs.
|
|
* Includes the changed content Uri when available.
|
|
* <p>
|
|
* Subclasses should override this method to handle content changes. To
|
|
* ensure correct operation on older versions of the framework that did not
|
|
* provide richer arguments, applications should implement all overloads.
|
|
* <p>
|
|
* Example implementation:
|
|
* <pre><code>
|
|
* // Implement the onChange(boolean) method to delegate the change notification to
|
|
* // the onChange(boolean, Uri) method to ensure correct operation on older versions
|
|
* // of the framework that did not have the onChange(boolean, Uri) method.
|
|
* {@literal @Override}
|
|
* public void onChange(boolean selfChange) {
|
|
* onChange(selfChange, null);
|
|
* }
|
|
*
|
|
* // Implement the onChange(boolean, Uri) method to take advantage of the new Uri argument.
|
|
* {@literal @Override}
|
|
* public void onChange(boolean selfChange, Uri uri) {
|
|
* // Handle change.
|
|
* }
|
|
* </code></pre>
|
|
* </p>
|
|
*
|
|
* @param selfChange True if this is a self-change notification.
|
|
* @param uri The Uri of the changed content.
|
|
*/
|
|
public void onChange(boolean selfChange, @Nullable Uri uri) {
|
|
onChange(selfChange);
|
|
}
|
|
|
|
/**
|
|
* This method is called when a content change occurs. Includes the changed
|
|
* content Uri when available.
|
|
* <p>
|
|
* Subclasses should override this method to handle content changes. To
|
|
* ensure correct operation on older versions of the framework that did not
|
|
* provide richer arguments, applications should implement all overloads.
|
|
*
|
|
* @param selfChange True if this is a self-change notification.
|
|
* @param uri The Uri of the changed content.
|
|
* @param flags Flags indicating details about this change.
|
|
*/
|
|
public void onChange(boolean selfChange, @Nullable Uri uri, @NotifyFlags int flags) {
|
|
onChange(selfChange, uri);
|
|
}
|
|
|
|
/**
|
|
* This method is called when a content change occurs. Includes the changed
|
|
* content Uris when available.
|
|
* <p>
|
|
* Subclasses should override this method to handle content changes. To
|
|
* ensure correct operation on older versions of the framework that did not
|
|
* provide richer arguments, applications should implement all overloads.
|
|
*
|
|
* @param selfChange True if this is a self-change notification.
|
|
* @param uris The Uris of the changed content.
|
|
* @param flags Flags indicating details about this change.
|
|
*/
|
|
public void onChange(boolean selfChange, @NonNull Collection<Uri> uris,
|
|
@NotifyFlags int flags) {
|
|
for (Uri uri : uris) {
|
|
onChange(selfChange, uri, flags);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method is called when a content change occurs. Includes the changed
|
|
* content Uris when available.
|
|
* <p>
|
|
* Subclasses should override this method to handle content changes. To
|
|
* ensure correct operation on older versions of the framework that did not
|
|
* provide richer arguments, applications should implement all overloads.
|
|
*
|
|
* @param selfChange True if this is a self-change notification.
|
|
* @param uris The Uris of the changed content.
|
|
* @param flags Flags indicating details about this change.
|
|
* @param user The corresponding {@link UserHandle} for the current notification.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public void onChange(boolean selfChange, @NonNull Collection<Uri> uris,
|
|
@NotifyFlags int flags, @NonNull UserHandle user) {
|
|
onChange(selfChange, uris, user.getIdentifier());
|
|
}
|
|
|
|
/** @hide */
|
|
public void onChange(boolean selfChange, @NonNull Collection<Uri> uris,
|
|
@NotifyFlags int flags, @UserIdInt int userId) {
|
|
// There are dozens of people relying on the hidden API inside the
|
|
// system UID, so hard-code the old behavior for all of them; for
|
|
// everyone else we gate based on a specific change
|
|
if (!isChangeEnabledAddContentObserverFlags()
|
|
|| android.os.Process.myUid() == android.os.Process.SYSTEM_UID) {
|
|
// Deliver userId through argument to preserve hidden API behavior
|
|
onChange(selfChange, uris, flags, UserHandle.of(userId));
|
|
} else {
|
|
onChange(selfChange, uris, flags);
|
|
}
|
|
}
|
|
|
|
@android.ravenwood.annotation.RavenwoodReplace
|
|
private static boolean isChangeEnabledAddContentObserverFlags() {
|
|
return CompatChanges.isChangeEnabled(ADD_CONTENT_OBSERVER_FLAGS);
|
|
}
|
|
|
|
private static boolean isChangeEnabledAddContentObserverFlags$ravenwood() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Dispatches a change notification to the observer.
|
|
* <p>
|
|
* If a {@link Handler} was supplied to the {@link ContentObserver}
|
|
* constructor, then a call to the {@link #onChange} method is posted to the
|
|
* handler's message queue. Otherwise, the {@link #onChange} method is
|
|
* invoked immediately on this thread.
|
|
*
|
|
* @deprecated Callers should migrate towards using a richer overload that
|
|
* provides more details about the change, such as
|
|
* {@link #dispatchChange(boolean, Collection, int)}.
|
|
*/
|
|
@Deprecated
|
|
public final void dispatchChange(boolean selfChange) {
|
|
dispatchChange(selfChange, null);
|
|
}
|
|
|
|
/**
|
|
* Dispatches a change notification to the observer. Includes the changed
|
|
* content Uri when available.
|
|
* <p>
|
|
* If a {@link Handler} was supplied to the {@link ContentObserver}
|
|
* constructor, then a call to the {@link #onChange} method is posted to the
|
|
* handler's message queue. Otherwise, the {@link #onChange} method is
|
|
* invoked immediately on this thread.
|
|
*
|
|
* @param selfChange True if this is a self-change notification.
|
|
* @param uri The Uri of the changed content.
|
|
*/
|
|
public final void dispatchChange(boolean selfChange, @Nullable Uri uri) {
|
|
dispatchChange(selfChange, uri, 0);
|
|
}
|
|
|
|
/**
|
|
* Dispatches a change notification to the observer. Includes the changed
|
|
* content Uri when available.
|
|
* <p>
|
|
* If a {@link Handler} was supplied to the {@link ContentObserver}
|
|
* constructor, then a call to the {@link #onChange} method is posted to the
|
|
* handler's message queue. Otherwise, the {@link #onChange} method is
|
|
* invoked immediately on this thread.
|
|
*
|
|
* @param selfChange True if this is a self-change notification.
|
|
* @param uri The Uri of the changed content.
|
|
* @param flags Flags indicating details about this change.
|
|
*/
|
|
public final void dispatchChange(boolean selfChange, @Nullable Uri uri,
|
|
@NotifyFlags int flags) {
|
|
dispatchChange(selfChange, Arrays.asList(uri), flags);
|
|
}
|
|
|
|
/**
|
|
* Dispatches a change notification to the observer. Includes the changed
|
|
* content Uris when available.
|
|
* <p>
|
|
* If a {@link Handler} was supplied to the {@link ContentObserver}
|
|
* constructor, then a call to the {@link #onChange} method is posted to the
|
|
* handler's message queue. Otherwise, the {@link #onChange} method is
|
|
* invoked immediately on this thread.
|
|
*
|
|
* @param selfChange True if this is a self-change notification.
|
|
* @param uris The Uri of the changed content.
|
|
* @param flags Flags indicating details about this change.
|
|
*/
|
|
public final void dispatchChange(boolean selfChange, @NonNull Collection<Uri> uris,
|
|
@NotifyFlags int flags) {
|
|
dispatchChange(selfChange, uris, flags, UserHandle.getCallingUserId());
|
|
}
|
|
|
|
/** @hide */
|
|
public final void dispatchChange(boolean selfChange, @NonNull Collection<Uri> uris,
|
|
@NotifyFlags int flags, @UserIdInt int userId) {
|
|
if (mExecutor != null) {
|
|
mExecutor.execute(() -> {
|
|
onChange(selfChange, uris, flags, userId);
|
|
});
|
|
} else if (mHandler != null) {
|
|
// Supporting Handler directly rather than wrapping in a HandlerExecutor
|
|
// avoids introducing a RejectedExecutionException for legacy code when
|
|
// the post fails.
|
|
mHandler.post(() -> {
|
|
onChange(selfChange, uris, flags, userId);
|
|
});
|
|
} else {
|
|
onChange(selfChange, uris, flags, userId);
|
|
}
|
|
}
|
|
|
|
private static final class Transport extends IContentObserver.Stub {
|
|
private ContentObserver mContentObserver;
|
|
|
|
public Transport(ContentObserver contentObserver) {
|
|
mContentObserver = contentObserver;
|
|
}
|
|
|
|
@Override
|
|
public void onChange(boolean selfChange, Uri uri, int userId) {
|
|
// This is kept intact purely for apps using hidden APIs, to
|
|
// redirect to the updated implementation
|
|
onChangeEtc(selfChange, new Uri[] { uri }, 0, userId);
|
|
}
|
|
|
|
@Override
|
|
public void onChangeEtc(boolean selfChange, Uri[] uris, int flags, int userId) {
|
|
ContentObserver contentObserver = mContentObserver;
|
|
if (contentObserver != null) {
|
|
contentObserver.dispatchChange(selfChange, Arrays.asList(uris), flags, userId);
|
|
}
|
|
}
|
|
|
|
public void releaseContentObserver() {
|
|
mContentObserver = null;
|
|
}
|
|
}
|
|
}
|