From b9bba581a9d0f4be1f7cd22a7ba19f2c30413a0e Mon Sep 17 00:00:00 2001 From: Hall Liu Date: Tue, 6 Oct 2020 13:52:21 -0700 Subject: [PATCH] Expose the HomeVisibilityListener API Rename HomeVisibilityObserver to HomeVisibilityListener and expose it as module-only API. This listener is used by the SIM toolkit to detect when the home screen is visible. Also fix some bugs in the original implementation that were uncovered during CTS writing. Fixes: 170226810 Test: atest ActivityManagerTest#testHomeVisibilityListener Change-Id: I5d99f2bad144711d7135cc800921abf01df6235d --- api/module-lib-current.txt | 10 +++ api/test-current.txt | 7 +++ core/java/android/app/ActivityManager.java | 37 +++++++---- ...erver.java => HomeVisibilityListener.java} | 63 ++++++++++++------- non-updatable-api/module-lib-current.txt | 10 +++ 5 files changed, 91 insertions(+), 36 deletions(-) rename core/java/android/app/{HomeVisibilityObserver.java => HomeVisibilityListener.java} (57%) diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index 3801e1d34b063..2a831fded92c6 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -1,10 +1,20 @@ // Signature format: 2.0 package android.app { + public class ActivityManager { + method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener); + method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener); + } + public class AppOpsManager { field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage"; } + public abstract class HomeVisibilityListener { + ctor public HomeVisibilityListener(); + method public abstract void onHomeVisibilityChanged(boolean); + } + public class NotificationManager { method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle); field public static final String ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED = "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED"; diff --git a/api/test-current.txt b/api/test-current.txt index 68d28233ade04..ed385e6db5cee 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -77,6 +77,7 @@ package android.app { } public class ActivityManager { + method @RequiresPermission("android.permission.SET_ACTIVITY_WATCHER") public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int); method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName); method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String); @@ -86,6 +87,7 @@ package android.app { method @RequiresPermission("android.permission.INJECT_EVENTS") public void holdLock(int); method public static boolean isHighEndGfx(); method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void killProcessesWhenImperceptible(@NonNull int[], @NonNull String); + method @RequiresPermission("android.permission.SET_ACTIVITY_WATCHER") public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener); method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors(); method public static void resumeAppSwitches() throws android.os.RemoteException; @@ -456,6 +458,11 @@ package android.app { method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void stopDream(); } + public abstract class HomeVisibilityListener { + ctor public HomeVisibilityListener(); + method public abstract void onHomeVisibilityChanged(boolean); + } + public final class NotificationChannel implements android.os.Parcelable { method public int getOriginalImportance(); method public boolean isBlockable(); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index e75d2f60bb87f..30efb48cc1880 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -100,6 +100,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.concurrent.Executor; /** *

@@ -4752,31 +4753,43 @@ public class ActivityManager { } /** - * Register with {@link HomeVisibilityObserver} with ActivityManager. - * TODO: b/144351078 expose as SystemApi + * Register to be notified when the visibility of the home screen changes. + * + * @param executor The executor on which the listener should be called. + * @param listener The listener that is called when home visibility changes. * @hide */ - public void registerHomeVisibilityObserver(@NonNull HomeVisibilityObserver observer) { - Preconditions.checkNotNull(observer); + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @TestApi + @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) + public void addHomeVisibilityListener(@NonNull Executor executor, + @NonNull HomeVisibilityListener listener) { + Preconditions.checkNotNull(listener); + Preconditions.checkNotNull(executor); try { - observer.init(mContext, this); - getService().registerProcessObserver(observer.mObserver); + listener.init(mContext, executor, this); + getService().registerProcessObserver(listener.mObserver); // Notify upon first registration. - observer.onHomeVisibilityChanged(observer.mIsHomeActivityVisible); + executor.execute(() -> + listener.onHomeVisibilityChanged(listener.mIsHomeActivityVisible)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Unregister with {@link HomeVisibilityObserver} with ActivityManager. - * TODO: b/144351078 expose as SystemApi + * Removes a listener that was previously added with {@link #addHomeVisibilityListener}. + * + * @param listener The listener that was previously added. * @hide */ - public void unregisterHomeVisibilityObserver(@NonNull HomeVisibilityObserver observer) { - Preconditions.checkNotNull(observer); + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @TestApi + @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) + public void removeHomeVisibilityListener(@NonNull HomeVisibilityListener listener) { + Preconditions.checkNotNull(listener); try { - getService().unregisterProcessObserver(observer.mObserver); + getService().unregisterProcessObserver(listener.mObserver); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/HomeVisibilityObserver.java b/core/java/android/app/HomeVisibilityListener.java similarity index 57% rename from core/java/android/app/HomeVisibilityObserver.java rename to core/java/android/app/HomeVisibilityListener.java index 8422c6f6f8729..c6e5699c5fe2d 100644 --- a/core/java/android/app/HomeVisibilityObserver.java +++ b/core/java/android/app/HomeVisibilityListener.java @@ -16,49 +16,56 @@ package android.app; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.annotation.TestApi; import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; +import android.os.Binder; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.concurrent.Executor; /** - * An observer / callback to create and register by - * {@link ActivityManager#registerHomeVisibilityObserver} so that it's triggered when - * visibility of home page changes. - * TODO: b/144351078 expose as SystemApi + * A listener that will be invoked when the visibility of the home screen changes. + * Register this callback via {@link ActivityManager#addHomeVisibilityListener} * @hide */ -public abstract class HomeVisibilityObserver { +// This is a single-method listener that needs a bunch of supporting code, so it can't be an +// interface +@SuppressLint("ListenerInterface") +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +@TestApi +public abstract class HomeVisibilityListener { private Context mContext; private ActivityManager mActivityManager; + private Executor mExecutor; /** @hide */ - IProcessObserver.Stub mObserver; + android.app.IProcessObserver.Stub mObserver; /** @hide */ boolean mIsHomeActivityVisible; /** @hide */ - void init(Context context, ActivityManager activityManager) { + void init(Context context, Executor executor, ActivityManager activityManager) { mContext = context; mActivityManager = activityManager; mIsHomeActivityVisible = isHomeActivityVisible(); + mExecutor = executor; } /** - * The API that needs implemented and will be triggered when activity on home page changes. + * Called when the visibility of the home screen changes. + * + * @param isHomeActivityVisible Whether the home screen activity is now visible. */ public abstract void onHomeVisibilityChanged(boolean isHomeActivityVisible); - public HomeVisibilityObserver() { - mObserver = new IProcessObserver.Stub() { + public HomeVisibilityListener() { + mObserver = new android.app.IProcessObserver.Stub() { @Override public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) { - boolean isHomeActivityVisible = isHomeActivityVisible(); - if (mIsHomeActivityVisible != isHomeActivityVisible) { - mIsHomeActivityVisible = isHomeActivityVisible; - onHomeVisibilityChanged(mIsHomeActivityVisible); - } + refreshHomeVisibility(); } @Override @@ -67,6 +74,17 @@ public abstract class HomeVisibilityObserver { @Override public void onProcessDied(int pid, int uid) { + refreshHomeVisibility(); + } + + private void refreshHomeVisibility() { + boolean isHomeActivityVisible = isHomeActivityVisible(); + if (mIsHomeActivityVisible != isHomeActivityVisible) { + mIsHomeActivityVisible = isHomeActivityVisible; + Binder.withCleanCallingIdentity(() -> + mExecutor.execute(() -> + onHomeVisibilityChanged(mIsHomeActivityVisible))); + } } }; } @@ -83,12 +101,9 @@ public abstract class HomeVisibilityObserver { } // We can assume that the screen is idle if the home application is in the foreground. - final Intent intent = new Intent(Intent.ACTION_MAIN, null); - intent.addCategory(Intent.CATEGORY_HOME); - - ResolveInfo info = mContext.getPackageManager().resolveActivity(intent, - PackageManager.MATCH_DEFAULT_ONLY); - if (info != null && top.equals(info.activityInfo.packageName)) { + String defaultHomePackage = mContext.getPackageManager() + .getHomeActivities(new ArrayList<>()).getPackageName(); + if (Objects.equals(top, defaultHomePackage)) { return true; } diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt index 003a36316c9b8..a5d8493f37bf0 100644 --- a/non-updatable-api/module-lib-current.txt +++ b/non-updatable-api/module-lib-current.txt @@ -1,10 +1,20 @@ // Signature format: 2.0 package android.app { + public class ActivityManager { + method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener); + method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener); + } + public class AppOpsManager { field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage"; } + public abstract class HomeVisibilityListener { + ctor public HomeVisibilityListener(); + method public abstract void onHomeVisibilityChanged(boolean); + } + public class NotificationManager { method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle); field public static final String ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED = "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED";