From ed473f45744a06729c67d80423b89d2a238642bf Mon Sep 17 00:00:00 2001 From: Hall Liu Date: Tue, 6 Oct 2020 13:52:21 -0700 Subject: [PATCH 1/2] 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 Exempt-From-Owner-Approval: Previously approved in ag/12775889 Change-Id: Ia44462e55ad71119cf356485c60c121d331c50d4 --- 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 dc92b52dc4669..b8b66879dbaec 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 18320cd210788..85be4ec74baeb 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -79,6 +79,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); @@ -88,6 +89,7 @@ package android.app { method @RequiresPermission(android.Manifest.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; @@ -458,6 +460,11 @@ package android.app { method @RequiresPermission(android.Manifest.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 b19ce48865899..e135a3b734cfe 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"; From 716a7de24892d45dcc6fabf59224e76102ab2d5c Mon Sep 17 00:00:00 2001 From: Hall Liu Date: Tue, 13 Oct 2020 17:15:01 -0700 Subject: [PATCH 2/2] Adapt to the new test-current.txt generator Bug: 170776449 Test: build Change-Id: Ia0fb0568ce36ed9e8ac71d3c1154b008fc71d093 --- api/test-current.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/test-current.txt b/api/test-current.txt index 85be4ec74baeb..59ed57c94f223 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -79,7 +79,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.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); @@ -89,7 +89,7 @@ package android.app { method @RequiresPermission(android.Manifest.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.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;