diff options
1072 files changed, 21203 insertions, 10987 deletions
diff --git a/StubLibraries.bp b/StubLibraries.bp index 0fe34fb650eb..2bd5aee0cd24 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -311,15 +311,6 @@ java_defaults { compile_dex: true, } -java_defaults { - name: "android_stubs_dists_default", - dist: { - targets: ["sdk", "win_sdk"], - tag: ".jar", - dest: "android.jar", - }, -} - java_library_static { name: "android_monolith_stubs_current", srcs: [ ":api-stubs-docs" ], @@ -355,21 +346,7 @@ java_library_static { name: "android_system_monolith_stubs_current", srcs: [ ":system-api-stubs-docs" ], static_libs: [ "private-stub-annotations-jar" ], - defaults: [ - "android_defaults_stubs_current", - "android_stubs_dists_default", - ], - dist: { - dir: "apistubs/android/system", - }, - dists: [ - { - // Legacy dist path - targets: ["sdk", "win_sdk"], - tag: ".jar", - dest: "android_system.jar", - }, - ], + defaults: ["android_defaults_stubs_current"], } java_library_static { @@ -401,34 +378,14 @@ java_library_static { name: "android_test_stubs_current", srcs: [ ":test-api-stubs-docs" ], static_libs: [ "private-stub-annotations-jar" ], - defaults: [ - "android_defaults_stubs_current", - "android_stubs_dists_default", - ], - dist: { - dir: "apistubs/android/test", - }, - dists: [ - { - // Legacy dist path - targets: ["sdk", "win_sdk"], - tag: ".jar", - dest: "android_test.jar", - }, - ], + defaults: ["android_defaults_stubs_current"], } java_library_static { name: "android_module_lib_stubs_current", srcs: [ ":module-lib-api-stubs-docs-non-updatable" ], - defaults: [ - "android_defaults_stubs_current", - "android_stubs_dists_default", - ], + defaults: ["android_defaults_stubs_current"], libs: ["sdk_system_29_android"], - dist: { - dir: "apistubs/android/module-lib", - }, } java_library_static { diff --git a/apct-tests/perftests/contentcapture/Android.bp b/apct-tests/perftests/contentcapture/Android.bp index 66d7348008ab..19a66ad9f27b 100644 --- a/apct-tests/perftests/contentcapture/Android.bp +++ b/apct-tests/perftests/contentcapture/Android.bp @@ -20,9 +20,10 @@ android_test { "androidx.test.rules", "androidx.annotation_annotation", "apct-perftests-utils", - "collector-device-lib-platform", + "collector-device-lib", "compatibility-device-util-axt", ], platform_apis: true, test_suites: ["device-tests"], + data: [":perfetto_artifacts"], } diff --git a/apct-tests/perftests/contentcapture/AndroidManifest.xml b/apct-tests/perftests/contentcapture/AndroidManifest.xml index ee5577f265fa..80957c78abf3 100644 --- a/apct-tests/perftests/contentcapture/AndroidManifest.xml +++ b/apct-tests/perftests/contentcapture/AndroidManifest.xml @@ -16,11 +16,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.perftests.contentcapture"> - <uses-sdk android:targetSdkVersion="28" /> - - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - <uses-permission android:name="android.permission.REAL_GET_TASKS" /> - <application> <uses-library android:name="android.test.runner" /> <activity android:name="android.view.contentcapture.CustomTestActivity" diff --git a/apct-tests/perftests/contentcapture/AndroidTest.xml b/apct-tests/perftests/contentcapture/AndroidTest.xml index d2386bb1efea..d8e0a17d3935 100644 --- a/apct-tests/perftests/contentcapture/AndroidTest.xml +++ b/apct-tests/perftests/contentcapture/AndroidTest.xml @@ -21,7 +21,39 @@ <option name="test-file-name" value="ContentCapturePerfTests.apk" /> </target_preparer> + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" /> + </target_preparer> + + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path" /> + </metrics_collector> + + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="com.android.perftests.contentcapture" /> + + <!-- Listener related args for collecting the traces and waiting for the device to stabilize. --> + <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" /> + <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. --> + <option name="instrumentation-arg" key="newRunListenerMode" value="true" /> + + <!-- ProcLoadListener related arguments --> + <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run --> + <option name="instrumentation-arg" key="procload-collector:per_run" value="true" /> + <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" /> + <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" /> + <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" /> + + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" /> + <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" /> + + <option name="instrumentation-arg" key="newRunListenerMode" value="true" /> </test> </configuration> diff --git a/apct-tests/perftests/windowmanager/Android.bp b/apct-tests/perftests/windowmanager/Android.bp index 9fa99853ace3..dbfe6ab3210f 100644 --- a/apct-tests/perftests/windowmanager/Android.bp +++ b/apct-tests/perftests/windowmanager/Android.bp @@ -23,6 +23,7 @@ android_test { "platform-test-annotations", ], test_suites: ["device-tests"], + data: [":perfetto_artifacts"], platform_apis: true, certificate: "platform", } diff --git a/apct-tests/perftests/windowmanager/AndroidTest.xml b/apct-tests/perftests/windowmanager/AndroidTest.xml index 0a80cf96fba3..aee02c7ab767 100644 --- a/apct-tests/perftests/windowmanager/AndroidTest.xml +++ b/apct-tests/perftests/windowmanager/AndroidTest.xml @@ -28,14 +28,42 @@ <option name="run-command" value="cmd package compile -m speed com.android.perftests.wm" /> </target_preparer> + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" /> + </target_preparer> + + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="com.android.perftests.wm" /> <option name="hidden-api-checks" value="false"/> - <option name="device-listeners" value="android.wm.WmPerfRunListener" /> + + <!-- Listener related args for collecting the traces and waiting for the device to stabilize. --> + <option name="device-listeners" value="android.wm.WmPerfRunListener,android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" /> + + <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. --> + <option name="instrumentation-arg" key="newRunListenerMode" value="true" /> + + <!-- ProcLoadListener related arguments --> + <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run --> + <option name="instrumentation-arg" key="procload-collector:per_run" value="true" /> + <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" /> + <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" /> + <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" /> + + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" /> + <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" /> + + <option name="instrumentation-arg" key="newRunListenerMode" value="true" /> </test> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> - <option name="directory-keys" value="/data/local/WmPerfTests" /> - <option name="collect-on-run-ended-only" value="true" /> + <option name="directory-keys" value="/data/local/tmp/WmPerfTests" /> + <!-- Needed for pulling the collected trace config on to the host --> + <option name="pull-pattern-keys" value="perfetto_file_path" /> </metrics_collector> </configuration> diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java index dc6245bf2c09..cc0e939a2a80 100644 --- a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java +++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java @@ -55,7 +55,7 @@ public class WindowManagerPerfTestBase { * is in /data because while enabling method profling of system server, it cannot write the * trace to external storage. */ - static final File BASE_OUT_PATH = new File("/data/local/WmPerfTests"); + static final File BASE_OUT_PATH = new File("/data/local/tmp/WmPerfTests"); @BeforeClass public static void setUpOnce() { diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java index c276ae1fe45e..6e644ffb8b27 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java +++ b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java @@ -168,7 +168,9 @@ public final class SearchSpec { /** Sets the maximum number of results to retrieve from the query */ @NonNull public SearchSpec.Builder setNumToRetrieve(int numToRetrieve) { - mResultSpecBuilder.setNumToRetrieve(numToRetrieve); + // Just retrieve everything in one page. + // TODO(b/152359656): Realign these two apis properly. + mResultSpecBuilder.setNumPerPage(numToRetrieve); return this; } diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java index 876d73a570af..8e26052ee08b 100644 --- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java +++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java @@ -125,10 +125,11 @@ public class AppStateTrackerImpl implements AppStateTracker { private int[] mTempExemptAppIds = mPowerExemptAllAppIds; /** - * Per-user packages that are in the EXEMPT bucket. + * Per-user packages that are in the EXEMPTED bucket. */ @GuardedBy("mLock") - private final SparseSetArray<String> mExemptBucketPackages = new SparseSetArray<>(); + @VisibleForTesting + final SparseSetArray<String> mExemptedBucketPackages = new SparseSetArray<>(); @GuardedBy("mLock") final ArraySet<Listener> mListeners = new ArraySet<>(); @@ -180,7 +181,7 @@ public class AppStateTrackerImpl implements AppStateTracker { int ALL_UNEXEMPTED = 3; int ALL_EXEMPTION_LIST_CHANGED = 4; int TEMP_EXEMPTION_LIST_CHANGED = 5; - int EXEMPT_BUCKET_CHANGED = 6; + int EXEMPTED_BUCKET_CHANGED = 6; int FORCE_ALL_CHANGED = 7; int FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8; @@ -195,7 +196,7 @@ public class AppStateTrackerImpl implements AppStateTracker { "ALL_UNEXEMPTED", "ALL_EXEMPTION_LIST_CHANGED", "TEMP_EXEMPTION_LIST_CHANGED", - "EXEMPT_BUCKET_CHANGED", + "EXEMPTED_BUCKET_CHANGED", "FORCE_ALL_CHANGED", "FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED", @@ -338,9 +339,9 @@ public class AppStateTrackerImpl implements AppStateTracker { } /** - * This is called when the EXEMPT bucket is updated. + * This is called when the EXEMPTED bucket is updated. */ - private void onExemptBucketChanged(AppStateTrackerImpl sender) { + private void onExemptedBucketChanged(AppStateTrackerImpl sender) { // This doesn't happen very often, so just re-evaluate all jobs / alarms. updateAllJobs(); unblockAllUnrestrictedAlarms(); @@ -424,6 +425,38 @@ public class AppStateTrackerImpl implements AppStateTracker { mHandler = new MyHandler(looper); } + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + switch (intent.getAction()) { + case Intent.ACTION_USER_REMOVED: + if (userId > 0) { + mHandler.doUserRemoved(userId); + } + break; + case Intent.ACTION_BATTERY_CHANGED: + synchronized (mLock) { + mIsPluggedIn = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0); + } + updateForceAllAppStandbyState(); + break; + case Intent.ACTION_PACKAGE_REMOVED: + if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + final String pkgName = intent.getData().getSchemeSpecificPart(); + final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + // No need to notify for state change as all the alarms and jobs should be + // removed too. + mExemptedBucketPackages.remove(userId, pkgName); + mRunAnyRestrictedPackages.remove(Pair.create(uid, pkgName)); + mActiveUids.delete(uid); + mForegroundUids.delete(uid); + } + break; + } + } + }; + /** * Call it when the system is ready. */ @@ -465,8 +498,11 @@ public class AppStateTrackerImpl implements AppStateTracker { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_REMOVED); filter.addAction(Intent.ACTION_BATTERY_CHANGED); - filter.addAction(Intent.ACTION_PACKAGE_REMOVED); - mContext.registerReceiver(new MyReceiver(), filter); + mContext.registerReceiver(mReceiver, filter); + + filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); + filter.addDataScheme(IntentFilter.SCHEME_PACKAGE); + mContext.registerReceiver(mReceiver, filter); refreshForcedAppStandbyUidPackagesLocked(); @@ -693,30 +729,6 @@ public class AppStateTrackerImpl implements AppStateTracker { } } - private final class MyReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) { - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - if (userId > 0) { - mHandler.doUserRemoved(userId); - } - } else if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { - synchronized (mLock) { - mIsPluggedIn = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0); - } - updateForceAllAppStandbyState(); - } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction()) - && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - final String pkgName = intent.getData().getSchemeSpecificPart(); - if (mExemptBucketPackages.remove(userId, pkgName)) { - mHandler.notifyExemptBucketChanged(); - } - } - } - } - final class StandbyTracker extends AppIdleStateChangeListener { @Override public void onAppIdleStateChanged(String packageName, int userId, boolean idle, @@ -728,12 +740,12 @@ public class AppStateTrackerImpl implements AppStateTracker { synchronized (mLock) { final boolean changed; if (bucket == UsageStatsManager.STANDBY_BUCKET_EXEMPTED) { - changed = mExemptBucketPackages.add(userId, packageName); + changed = mExemptedBucketPackages.add(userId, packageName); } else { - changed = mExemptBucketPackages.remove(userId, packageName); + changed = mExemptedBucketPackages.remove(userId, packageName); } if (changed) { - mHandler.notifyExemptBucketChanged(); + mHandler.notifyExemptedBucketChanged(); } } } @@ -755,7 +767,7 @@ public class AppStateTrackerImpl implements AppStateTracker { private static final int MSG_FORCE_ALL_CHANGED = 7; private static final int MSG_USER_REMOVED = 8; private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 9; - private static final int MSG_EXEMPT_BUCKET_CHANGED = 10; + private static final int MSG_EXEMPTED_BUCKET_CHANGED = 10; private static final int MSG_ON_UID_STATE_CHANGED = 11; private static final int MSG_ON_UID_ACTIVE = 12; @@ -803,9 +815,9 @@ public class AppStateTrackerImpl implements AppStateTracker { obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget(); } - public void notifyExemptBucketChanged() { - removeMessages(MSG_EXEMPT_BUCKET_CHANGED); - obtainMessage(MSG_EXEMPT_BUCKET_CHANGED).sendToTarget(); + public void notifyExemptedBucketChanged() { + removeMessages(MSG_EXEMPTED_BUCKET_CHANGED); + obtainMessage(MSG_EXEMPTED_BUCKET_CHANGED).sendToTarget(); } public void doUserRemoved(int userId) { @@ -888,11 +900,11 @@ public class AppStateTrackerImpl implements AppStateTracker { mStatLogger.logDurationStat(Stats.TEMP_EXEMPTION_LIST_CHANGED, start); return; - case MSG_EXEMPT_BUCKET_CHANGED: + case MSG_EXEMPTED_BUCKET_CHANGED: for (Listener l : cloneListeners()) { - l.onExemptBucketChanged(sender); + l.onExemptedBucketChanged(sender); } - mStatLogger.logDurationStat(Stats.EXEMPT_BUCKET_CHANGED, start); + mStatLogger.logDurationStat(Stats.EXEMPTED_BUCKET_CHANGED, start); return; case MSG_FORCE_ALL_CHANGED: @@ -1005,7 +1017,7 @@ public class AppStateTrackerImpl implements AppStateTracker { } cleanUpArrayForUser(mActiveUids, removedUserId); cleanUpArrayForUser(mForegroundUids, removedUserId); - mExemptBucketPackages.remove(removedUserId); + mExemptedBucketPackages.remove(removedUserId); } } @@ -1148,7 +1160,7 @@ public class AppStateTrackerImpl implements AppStateTracker { } final int userId = UserHandle.getUserId(uid); if (mAppStandbyInternal.isAppIdleEnabled() && !mAppStandbyInternal.isInParole() - && mExemptBucketPackages.contains(userId, packageName)) { + && mExemptedBucketPackages.contains(userId, packageName)) { return false; } return mForceAllAppsStandby; @@ -1301,14 +1313,14 @@ public class AppStateTrackerImpl implements AppStateTracker { pw.println("Exempted bucket packages:"); pw.increaseIndent(); - for (int i = 0; i < mExemptBucketPackages.size(); i++) { + for (int i = 0; i < mExemptedBucketPackages.size(); i++) { pw.print("User "); - pw.print(mExemptBucketPackages.keyAt(i)); + pw.print(mExemptedBucketPackages.keyAt(i)); pw.println(); pw.increaseIndent(); - for (int j = 0; j < mExemptBucketPackages.sizeAt(i); j++) { - pw.print(mExemptBucketPackages.valueAt(i, j)); + for (int j = 0; j < mExemptedBucketPackages.sizeAt(i); j++) { + pw.print(mExemptedBucketPackages.valueAt(i, j)); pw.println(); } pw.decreaseIndent(); @@ -1384,12 +1396,13 @@ public class AppStateTrackerImpl implements AppStateTracker { proto.write(AppStateTrackerProto.TEMP_POWER_SAVE_EXEMPT_APP_IDS, appId); } - for (int i = 0; i < mExemptBucketPackages.size(); i++) { - for (int j = 0; j < mExemptBucketPackages.sizeAt(i); j++) { + for (int i = 0; i < mExemptedBucketPackages.size(); i++) { + for (int j = 0; j < mExemptedBucketPackages.sizeAt(i); j++) { final long token2 = proto.start(AppStateTrackerProto.EXEMPTED_BUCKET_PACKAGES); - proto.write(ExemptedPackage.USER_ID, mExemptBucketPackages.keyAt(i)); - proto.write(ExemptedPackage.PACKAGE_NAME, mExemptBucketPackages.valueAt(i, j)); + proto.write(ExemptedPackage.USER_ID, mExemptedBucketPackages.keyAt(i)); + proto.write(ExemptedPackage.PACKAGE_NAME, + mExemptedBucketPackages.valueAt(i, j)); proto.end(token2); } diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 4194fd0068c2..e2f13c560879 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -4310,7 +4310,7 @@ public class AlarmManagerService extends SystemService { filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); - filter.addDataScheme("package"); + filter.addDataScheme(IntentFilter.SCHEME_PACKAGE); getContext().registerReceiver(this, filter); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); diff --git a/api/current.txt b/api/current.txt index 17fbb55d2f91..9e0d88ecdfd1 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5596,6 +5596,7 @@ package android.app { method public android.graphics.drawable.Icon getIcon(); method public android.app.RemoteInput[] getRemoteInputs(); method public int getSemanticAction(); + method public boolean isAuthenticationRequired(); method public boolean isContextual(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.Notification.Action> CREATOR; @@ -5625,6 +5626,7 @@ package android.app { method @NonNull public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Extender); method @NonNull public android.os.Bundle getExtras(); method @NonNull public android.app.Notification.Action.Builder setAllowGeneratedReplies(boolean); + method @NonNull public android.app.Notification.Action.Builder setAuthenticationRequired(boolean); method @NonNull public android.app.Notification.Action.Builder setContextual(boolean); method @NonNull public android.app.Notification.Action.Builder setSemanticAction(int); } @@ -45974,7 +45976,9 @@ package android.telecom { method public final void addExistingConnection(android.telecom.PhoneAccountHandle, android.telecom.Connection); method public final void conferenceRemoteConnections(android.telecom.RemoteConnection, android.telecom.RemoteConnection); method public final void connectionServiceFocusReleased(); + method @Nullable public final android.telecom.RemoteConference createRemoteIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); method public final android.telecom.RemoteConnection createRemoteIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); + method @Nullable public final android.telecom.RemoteConference createRemoteOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); method public final android.telecom.RemoteConnection createRemoteOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public final java.util.Collection<android.telecom.Conference> getAllConferences(); method public final java.util.Collection<android.telecom.Connection> getAllConnections(); @@ -46205,6 +46209,7 @@ package android.telecom { public final class RemoteConnection { method public void abort(); + method public void addConferenceParticipants(@NonNull java.util.List<android.net.Uri>); method public void answer(); method public void disconnect(); method public android.net.Uri getAddress(); @@ -46982,7 +46987,7 @@ package android.telephony { method public long getNci(); method @IntRange(from=0, to=3279165) public int getNrarfcn(); method @IntRange(from=0, to=1007) public int getPci(); - method @IntRange(from=0, to=65535) public int getTac(); + method @IntRange(from=0, to=16777215) public int getTac(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityNr> CREATOR; } @@ -65722,7 +65727,7 @@ package java.lang.reflect { package java.math { - public class BigDecimal extends java.lang.Number implements java.lang.Comparable<java.math.BigDecimal> java.io.Serializable { + public class BigDecimal extends java.lang.Number implements java.lang.Comparable<java.math.BigDecimal> { ctor public BigDecimal(char[], int, int); ctor public BigDecimal(char[], int, int, java.math.MathContext); ctor public BigDecimal(char[]); @@ -65809,19 +65814,20 @@ package java.math { field public static final java.math.BigDecimal ZERO; } - public class BigInteger extends java.lang.Number implements java.lang.Comparable<java.math.BigInteger> java.io.Serializable { + public class BigInteger extends java.lang.Number implements java.lang.Comparable<java.math.BigInteger> { + ctor public BigInteger(byte[]); + ctor public BigInteger(int, byte[]); + ctor public BigInteger(@NonNull String, int); + ctor public BigInteger(@NonNull String); ctor public BigInteger(int, @NonNull java.util.Random); ctor public BigInteger(int, int, @NonNull java.util.Random); - ctor public BigInteger(@NonNull String); - ctor public BigInteger(@NonNull String, int); - ctor public BigInteger(int, byte[]); - ctor public BigInteger(byte[]); method @NonNull public java.math.BigInteger abs(); method @NonNull public java.math.BigInteger add(@NonNull java.math.BigInteger); method @NonNull public java.math.BigInteger and(@NonNull java.math.BigInteger); method @NonNull public java.math.BigInteger andNot(@NonNull java.math.BigInteger); method public int bitCount(); method public int bitLength(); + method public byte byteValueExact(); method @NonNull public java.math.BigInteger clearBit(int); method public int compareTo(@NonNull java.math.BigInteger); method @NonNull public java.math.BigInteger divide(@NonNull java.math.BigInteger); @@ -65832,8 +65838,10 @@ package java.math { method @NonNull public java.math.BigInteger gcd(@NonNull java.math.BigInteger); method public int getLowestSetBit(); method public int intValue(); + method public int intValueExact(); method public boolean isProbablePrime(int); method public long longValue(); + method public long longValueExact(); method @NonNull public java.math.BigInteger max(@NonNull java.math.BigInteger); method @NonNull public java.math.BigInteger min(@NonNull java.math.BigInteger); method @NonNull public java.math.BigInteger mod(@NonNull java.math.BigInteger); @@ -65850,6 +65858,7 @@ package java.math { method @NonNull public java.math.BigInteger setBit(int); method @NonNull public java.math.BigInteger shiftLeft(int); method @NonNull public java.math.BigInteger shiftRight(int); + method public short shortValueExact(); method public int signum(); method @NonNull public java.math.BigInteger subtract(@NonNull java.math.BigInteger); method public boolean testBit(int); diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index b587ea1f3b74..17545a469cb8 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -7,6 +7,7 @@ package android.app { 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"; } } @@ -45,6 +46,13 @@ package android.media.session { field public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 65536; // 0x10000 } + public final class MediaSessionManager { + method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent); + method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); + method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int); + method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); + } + } package android.net { diff --git a/api/system-current.txt b/api/system-current.txt index b011e9af40a0..3ab164554da6 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -301,6 +301,7 @@ package android { field public static final int config_helpIntentNameKey = 17039390; // 0x104001e field public static final int config_helpPackageNameKey = 17039387; // 0x104001b field public static final int config_helpPackageNameValue = 17039388; // 0x104001c + field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 field public static final int config_systemGallery = 17039399; // 0x1040027 } @@ -4195,7 +4196,8 @@ package android.media { public class AudioManager { method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes); - method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException; + method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException; + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException; method public void clearAudioServerStateCallback(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); @@ -4207,13 +4209,15 @@ package android.media { method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); + method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages(); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method public boolean isAudioServerRunning(); method public boolean isHdmiSystemAudioSupported(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy); method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback); - method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener); + method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException; method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException; @@ -4223,6 +4227,7 @@ package android.media { method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy); @@ -4246,8 +4251,12 @@ package android.media { method public void onAudioServerUp(); } - public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener { - method public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes); + @Deprecated public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener { + method @Deprecated public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes); + } + + public static interface AudioManager.OnPreferredDevicesForStrategyChangedListener { + method public void onPreferredDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>); } public abstract static class AudioManager.VolumeGroupCallback { @@ -12162,6 +12171,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); + field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43 field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0 diff --git a/api/test-current.txt b/api/test-current.txt index 963ef7c540c7..620c5b30b925 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -42,6 +42,7 @@ package android { public static final class R.string { field public static final int config_defaultAssistant = 17039393; // 0x1040021 field public static final int config_defaultDialer = 17039395; // 0x1040023 + field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 field public static final int config_systemGallery = 17039399; // 0x1040027 } @@ -969,6 +970,7 @@ package android.content.pm { method public boolean isSystemApp(); field public static final int PRIVATE_FLAG_PRIVILEGED = 8; // 0x8 field public int privateFlags; + field public int targetSandboxVersion; } public class LauncherApps { @@ -1016,6 +1018,7 @@ package android.content.pm { field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage"; field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption"; field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000 + field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20 field public static final int FLAG_PERMISSION_GRANTED_BY_ROLE = 32768; // 0x8000 field public static final int FLAG_PERMISSION_ONE_TIME = 65536; // 0x10000 field public static final int FLAG_PERMISSION_POLICY_FIXED = 4; // 0x4 @@ -1779,6 +1782,9 @@ package android.media { method public static float getMasterBalance(); method public static final int getNumStreamTypes(); method public static int setMasterBalance(float); + field public static final int DEVICE_ROLE_DISABLED = 2; // 0x2 + field public static final int DEVICE_ROLE_NONE = 0; // 0x0 + field public static final int DEVICE_ROLE_PREFERRED = 1; // 0x1 field public static final int STREAM_DEFAULT = -1; // 0xffffffff } @@ -4612,6 +4618,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); + field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43 field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0 diff --git a/cmds/incidentd/tests/section_list.cpp b/cmds/incidentd/tests/section_list.cpp index 3a45af028518..60f7fb788837 100644 --- a/cmds/incidentd/tests/section_list.cpp +++ b/cmds/incidentd/tests/section_list.cpp @@ -1,4 +1,4 @@ -// This file is a dummy section_list.cpp used for test only. +// This file is a stub section_list.cpp used for test only. #include "section_list.h" #include "frameworks/base/cmds/incidentd/tests/test_proto.pb.h" diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 7c419519a558..88db1d84df8e 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -73,10 +73,10 @@ cc_defaults { "src/HashableDimensionKey.cpp", "src/logd/LogEvent.cpp", "src/logd/LogEventQueue.cpp", - "src/matchers/CombinationLogMatchingTracker.cpp", + "src/matchers/CombinationAtomMatchingTracker.cpp", "src/matchers/EventMatcherWizard.cpp", "src/matchers/matcher_util.cpp", - "src/matchers/SimpleLogMatchingTracker.cpp", + "src/matchers/SimpleAtomMatchingTracker.cpp", "src/metadata_util.cpp", "src/metrics/CountMetricProducer.cpp", "src/metrics/duration_helper/MaxDurationTracker.cpp", diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index c95f4c07f86c..6b335ee1b923 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -4434,15 +4434,12 @@ message PrivacyIndicatorsInteracted { UNKNOWN = 0; CHIP_VIEWED = 1; CHIP_CLICKED = 2; - DIALOG_PRIVACY_SETTINGS = 3; + reserved 3; // Used only in beta builds, never shipped DIALOG_DISMISS = 4; DIALOG_LINE_ITEM = 5; } optional Type type = 1 [(state_field_option).exclusive_state = true]; - - // Used if the type is LINE_ITEM - optional string package_name = 2; } /** diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp index e9875baf58c7..7a1ece94fd3e 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp +++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp @@ -92,8 +92,8 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf mUnSlicedChildren.push_back(childIndex); } mChildren.push_back(childIndex); - mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(), - childTracker->getLogTrackerIndex().end()); + mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(), + childTracker->getAtomMatchingTrackerIndex().end()); } mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index 62736c8160bb..9da1af427e5f 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -18,7 +18,7 @@ #include "condition/condition_util.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "matchers/LogMatchingTracker.h" +#include "matchers/AtomMatchingTracker.h" #include "matchers/matcher_util.h" #include <utils/RefBase.h> @@ -60,9 +60,9 @@ public: // evaluate current condition given the new event. // event: the new log event - // eventMatcherValues: the results of the LogMatcherTrackers. LogMatcherTrackers always process - // event before ConditionTrackers, because ConditionTracker depends on - // LogMatchingTrackers. + // eventMatcherValues: the results of the AtomMatchingTrackers. AtomMatchingTrackers always + // process event before ConditionTrackers, because ConditionTracker depends + // on AtomMatchingTrackers. // mAllConditions: the list of all ConditionTracker // conditionCache: the cached non-sliced condition of the ConditionTrackers for this new event. // conditionChanged: the bit map to record whether the condition has changed. @@ -88,8 +88,8 @@ public: const bool isPartialLink, std::vector<ConditionState>& conditionCache) const = 0; - // return the list of LogMatchingTracker index that this ConditionTracker uses. - virtual const std::set<int>& getLogTrackerIndex() const { + // return the list of AtomMatchingTracker index that this ConditionTracker uses. + virtual const std::set<int>& getAtomMatchingTrackerIndex() const { return mTrackerIndex; } @@ -136,7 +136,7 @@ protected: // if it's properly initialized. bool mInitialized; - // the list of LogMatchingTracker index that this ConditionTracker uses. + // the list of AtomMatchingTracker index that this ConditionTracker uses. std::set<int> mTrackerIndex; // This variable is only used for CombinationConditionTrackers. diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/AtomMatchingTracker.h index 49a41add4560..5194f9970766 100644 --- a/cmds/statsd/src/matchers/LogMatchingTracker.h +++ b/cmds/statsd/src/matchers/AtomMatchingTracker.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef LOG_MATCHING_TRACKER_H -#define LOG_MATCHING_TRACKER_H +#ifndef ATOM_MATCHING_TRACKER_H +#define ATOM_MATCHING_TRACKER_H #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "logd/LogEvent.h" @@ -31,35 +31,36 @@ namespace android { namespace os { namespace statsd { -class LogMatchingTracker : public virtual RefBase { +class AtomMatchingTracker : public virtual RefBase { public: - LogMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash) + AtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash) : mId(id), mIndex(index), mInitialized(false), mProtoHash(protoHash){}; - virtual ~LogMatchingTracker(){}; + virtual ~AtomMatchingTracker(){}; - // Initialize this LogMatchingTracker. - // allLogMatchers: the list of the AtomMatcher proto config. This is needed because we don't - // store the proto object in memory. We only need it during initilization. - // allTrackers: the list of the LogMatchingTracker objects. It's a one-to-one mapping with - // allLogMatchers. This is needed because the initialization is done recursively - // for CombinationLogMatchingTrackers using DFS. + // Initialize this AtomMatchingTracker. + // allAtomMatchers: the list of the AtomMatcher proto config. This is needed because we don't + // store the proto object in memory. We only need it during initilization. + // allAtomMatchingTrackers: the list of the AtomMatchingTracker objects. It's a one-to-one + // mapping with allAtomMatchers. This is needed because the + // initialization is done recursively for + // CombinationAtomMatchingTrackers using DFS. // stack: a bit map to record which matcher has been visited on the stack. This is for detecting // circle dependency. - virtual bool init(const std::vector<AtomMatcher>& allLogMatchers, - const std::vector<sp<LogMatchingTracker>>& allTrackers, + virtual bool init(const std::vector<AtomMatcher>& allAtomMatchers, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack) = 0; // Called when a log event comes. // event: the log event. - // allTrackers: the list of all LogMatchingTrackers. This is needed because the log processing - // is done recursively. + // allAtomMatchingTrackers: the list of all AtomMatchingTrackers. This is needed because the log + // processing is done recursively. // matcherResults: The cached results for all matchers for this event. Parent matchers can // directly access the children's matching results if they have been evaluated. // Otherwise, call children matchers' onLogEvent. virtual void onLogEvent(const LogEvent& event, - const std::vector<sp<LogMatchingTracker>>& allTrackers, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, std::vector<MatchingState>& matcherResults) = 0; // Get the tagIds that this matcher cares about. The combined collection is stored @@ -81,23 +82,23 @@ protected: // Name of this matching. We don't really need the name, but it makes log message easy to debug. const int64_t mId; - // Index of this LogMatchingTracker in MetricsManager's container. + // Index of this AtomMatchingTracker in MetricsManager's container. const int mIndex; - // Whether this LogMatchingTracker has been properly initialized. + // Whether this AtomMatchingTracker has been properly initialized. bool mInitialized; - // The collection of the event tag ids that this LogMatchingTracker cares. So we can quickly + // The collection of the event tag ids that this AtomMatchingTracker cares. So we can quickly // return kNotMatched when we receive an event with an id not in the list. This is especially - // useful when we have a complex CombinationLogMatcherTracker. + // useful when we have a complex CombinationAtomMatchingTracker. std::set<int> mAtomIds; // Hash of the AtomMatcher's proto bytes from StatsdConfig. // Used to determine if the definition of this matcher has changed across a config update. const uint64_t mProtoHash; - FRIEND_TEST(MetricsManagerTest, TestCreateLogTrackerSimple); - FRIEND_TEST(MetricsManagerTest, TestCreateLogTrackerCombination); + FRIEND_TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerSimple); + FRIEND_TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerCombination); FRIEND_TEST(ConfigUpdateTest, TestUpdateMatchers); }; @@ -105,4 +106,4 @@ protected: } // namespace os } // namespace android -#endif // LOG_MATCHING_TRACKER_H +#endif // ATOM_MATCHING_TRACKER_H diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp index 60bcc26f0873..19637a449c44 100644 --- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp +++ b/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp @@ -16,7 +16,8 @@ #include "Log.h" -#include "CombinationLogMatchingTracker.h" +#include "CombinationAtomMatchingTracker.h" + #include "matchers/matcher_util.h" namespace android { @@ -27,18 +28,18 @@ using std::set; using std::unordered_map; using std::vector; -CombinationLogMatchingTracker::CombinationLogMatchingTracker(const int64_t& id, const int index, - const uint64_t protoHash) - : LogMatchingTracker(id, index, protoHash) { +CombinationAtomMatchingTracker::CombinationAtomMatchingTracker(const int64_t& id, const int index, + const uint64_t protoHash) + : AtomMatchingTracker(id, index, protoHash) { } -CombinationLogMatchingTracker::~CombinationLogMatchingTracker() { +CombinationAtomMatchingTracker::~CombinationAtomMatchingTracker() { } -bool CombinationLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatchers, - const vector<sp<LogMatchingTracker>>& allTrackers, - const unordered_map<int64_t, int>& matcherMap, - vector<bool>& stack) { +bool CombinationAtomMatchingTracker::init( + const vector<AtomMatcher>& allAtomMatchers, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const unordered_map<int64_t, int>& matcherMap, vector<bool>& stack) { if (mInitialized) { return true; } @@ -46,7 +47,7 @@ bool CombinationLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatche // mark this node as visited in the recursion stack. stack[mIndex] = true; - AtomMatcher_Combination matcher = allLogMatchers[mIndex].combination(); + AtomMatcher_Combination matcher = allAtomMatchers[mIndex].combination(); // LogicalOperation is missing in the config if (!matcher.has_operation()) { @@ -74,14 +75,15 @@ bool CombinationLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatche return false; } - if (!allTrackers[childIndex]->init(allLogMatchers, allTrackers, matcherMap, stack)) { + if (!allAtomMatchingTrackers[childIndex]->init(allAtomMatchers, allAtomMatchingTrackers, + matcherMap, stack)) { ALOGW("child matcher init failed %lld", (long long)child); return false; } mChildren.push_back(childIndex); - const set<int>& childTagIds = allTrackers[childIndex]->getAtomIds(); + const set<int>& childTagIds = allAtomMatchingTrackers[childIndex]->getAtomIds(); mAtomIds.insert(childTagIds.begin(), childTagIds.end()); } @@ -91,9 +93,9 @@ bool CombinationLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatche return true; } -void CombinationLogMatchingTracker::onLogEvent(const LogEvent& event, - const vector<sp<LogMatchingTracker>>& allTrackers, - vector<MatchingState>& matcherResults) { +void CombinationAtomMatchingTracker::onLogEvent( + const LogEvent& event, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + vector<MatchingState>& matcherResults) { // this event has been processed. if (matcherResults[mIndex] != MatchingState::kNotComputed) { return; @@ -107,8 +109,8 @@ void CombinationLogMatchingTracker::onLogEvent(const LogEvent& event, // evaluate children matchers if they haven't been evaluated. for (const int childIndex : mChildren) { if (matcherResults[childIndex] == MatchingState::kNotComputed) { - const sp<LogMatchingTracker>& child = allTrackers[childIndex]; - child->onLogEvent(event, allTrackers, matcherResults); + const sp<AtomMatchingTracker>& child = allAtomMatchingTrackers[childIndex]; + child->onLogEvent(event, allAtomMatchingTrackers, matcherResults); } } diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h b/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h index 6b8a7fb19cf0..06a4932804f5 100644 --- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h +++ b/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef COMBINATION_LOG_MATCHING_TRACKER_H -#define COMBINATION_LOG_MATCHING_TRACKER_H +#ifndef COMBINATION_ATOM_MATCHING_TRACKER_H +#define COMBINATION_ATOM_MATCHING_TRACKER_H #include <unordered_map> #include <vector> -#include "LogMatchingTracker.h" + +#include "AtomMatchingTracker.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" namespace android { @@ -26,19 +27,18 @@ namespace os { namespace statsd { // Represents a AtomMatcher_Combination in the StatsdConfig. -class CombinationLogMatchingTracker : public virtual LogMatchingTracker { +class CombinationAtomMatchingTracker : public virtual AtomMatchingTracker { public: - CombinationLogMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash); + CombinationAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash); - bool init(const std::vector<AtomMatcher>& allLogMatchers, - const std::vector<sp<LogMatchingTracker>>& allTrackers, - const std::unordered_map<int64_t, int>& matcherMap, - std::vector<bool>& stack); + bool init(const std::vector<AtomMatcher>& allAtomMatchers, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack); - ~CombinationLogMatchingTracker(); + ~CombinationAtomMatchingTracker(); void onLogEvent(const LogEvent& event, - const std::vector<sp<LogMatchingTracker>>& allTrackers, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, std::vector<MatchingState>& matcherResults) override; private: @@ -50,4 +50,4 @@ private: } // namespace statsd } // namespace os } // namespace android -#endif // COMBINATION_LOG_MATCHING_TRACKER_H +#endif // COMBINATION_ATOM_MATCHING_TRACKER_H diff --git a/cmds/statsd/src/matchers/EventMatcherWizard.h b/cmds/statsd/src/matchers/EventMatcherWizard.h index 57ec2b35ba32..5d780f2423d8 100644 --- a/cmds/statsd/src/matchers/EventMatcherWizard.h +++ b/cmds/statsd/src/matchers/EventMatcherWizard.h @@ -16,7 +16,7 @@ #pragma once -#include "LogMatchingTracker.h" +#include "AtomMatchingTracker.h" namespace android { namespace os { @@ -25,7 +25,7 @@ namespace statsd { class EventMatcherWizard : public virtual android::RefBase { public: EventMatcherWizard(){}; // for testing - EventMatcherWizard(const std::vector<sp<LogMatchingTracker>>& eventTrackers) + EventMatcherWizard(const std::vector<sp<AtomMatchingTracker>>& eventTrackers) : mAllEventMatchers(eventTrackers){}; virtual ~EventMatcherWizard(){}; @@ -33,7 +33,7 @@ public: MatchingState matchLogEvent(const LogEvent& event, int matcher_index); private: - std::vector<sp<LogMatchingTracker>> mAllEventMatchers; + std::vector<sp<AtomMatchingTracker>> mAllEventMatchers; }; } // namespace statsd diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp index ff47d35b36cc..86b148dd8657 100644 --- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp +++ b/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp @@ -17,7 +17,7 @@ #define DEBUG false // STOPSHIP if true #include "Log.h" -#include "SimpleLogMatchingTracker.h" +#include "SimpleAtomMatchingTracker.h" namespace android { namespace os { @@ -26,11 +26,11 @@ namespace statsd { using std::unordered_map; using std::vector; -SimpleLogMatchingTracker::SimpleLogMatchingTracker(const int64_t& id, const int index, - const uint64_t protoHash, - const SimpleAtomMatcher& matcher, - const sp<UidMap>& uidMap) - : LogMatchingTracker(id, index, protoHash), mMatcher(matcher), mUidMap(uidMap) { +SimpleAtomMatchingTracker::SimpleAtomMatchingTracker(const int64_t& id, const int index, + const uint64_t protoHash, + const SimpleAtomMatcher& matcher, + const sp<UidMap>& uidMap) + : AtomMatchingTracker(id, index, protoHash), mMatcher(matcher), mUidMap(uidMap) { if (!matcher.has_atom_id()) { mInitialized = false; } else { @@ -39,20 +39,20 @@ SimpleLogMatchingTracker::SimpleLogMatchingTracker(const int64_t& id, const int } } -SimpleLogMatchingTracker::~SimpleLogMatchingTracker() { +SimpleAtomMatchingTracker::~SimpleAtomMatchingTracker() { } -bool SimpleLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatchers, - const vector<sp<LogMatchingTracker>>& allTrackers, - const unordered_map<int64_t, int>& matcherMap, - vector<bool>& stack) { +bool SimpleAtomMatchingTracker::init(const vector<AtomMatcher>& allAtomMatchers, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const unordered_map<int64_t, int>& matcherMap, + vector<bool>& stack) { // no need to do anything. return mInitialized; } -void SimpleLogMatchingTracker::onLogEvent(const LogEvent& event, - const vector<sp<LogMatchingTracker>>& allTrackers, - vector<MatchingState>& matcherResults) { +void SimpleAtomMatchingTracker::onLogEvent( + const LogEvent& event, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + vector<MatchingState>& matcherResults) { if (matcherResults[mIndex] != MatchingState::kNotComputed) { VLOG("Matcher %lld already evaluated ", (long long)mId); return; @@ -65,7 +65,7 @@ void SimpleLogMatchingTracker::onLogEvent(const LogEvent& event, bool matched = matchesSimple(mUidMap, mMatcher, event); matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched; - VLOG("Stats SimpleLogMatcher %lld matched? %d", (long long)mId, matched); + VLOG("Stats SimpleAtomMatcher %lld matched? %d", (long long)mId, matched); } } // namespace statsd diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h index e58e01252ade..49cbe09f8482 100644 --- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h +++ b/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h @@ -14,12 +14,13 @@ * limitations under the License. */ -#ifndef SIMPLE_LOG_MATCHING_TRACKER_H -#define SIMPLE_LOG_MATCHING_TRACKER_H +#ifndef SIMPLE_ATOM_MATCHING_TRACKER_H +#define SIMPLE_ATOM_MATCHING_TRACKER_H #include <unordered_map> #include <vector> -#include "LogMatchingTracker.h" + +#include "AtomMatchingTracker.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "packages/UidMap.h" @@ -27,20 +28,20 @@ namespace android { namespace os { namespace statsd { -class SimpleLogMatchingTracker : public virtual LogMatchingTracker { +class SimpleAtomMatchingTracker : public virtual AtomMatchingTracker { public: - SimpleLogMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash, - const SimpleAtomMatcher& matcher, const sp<UidMap>& uidMap); + SimpleAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash, + const SimpleAtomMatcher& matcher, const sp<UidMap>& uidMap); - ~SimpleLogMatchingTracker(); + ~SimpleAtomMatchingTracker(); - bool init(const std::vector<AtomMatcher>& allLogMatchers, - const std::vector<sp<LogMatchingTracker>>& allTrackers, + bool init(const std::vector<AtomMatcher>& allAtomMatchers, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack) override; void onLogEvent(const LogEvent& event, - const std::vector<sp<LogMatchingTracker>>& allTrackers, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, std::vector<MatchingState>& matcherResults) override; private: @@ -51,4 +52,4 @@ private: } // namespace statsd } // namespace os } // namespace android -#endif // SIMPLE_LOG_MATCHING_TRACKER_H +#endif // SIMPLE_ATOM_MATCHING_TRACKER_H diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp index e6d91223308d..a7454c5d923b 100644 --- a/cmds/statsd/src/matchers/matcher_util.cpp +++ b/cmds/statsd/src/matchers/matcher_util.cpp @@ -17,7 +17,7 @@ #include "Log.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "matchers/LogMatchingTracker.h" +#include "matchers/AtomMatchingTracker.h" #include "matchers/matcher_util.h" #include "stats_util.h" diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index 2fc772b6b641..ef3a24a43dcc 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -170,14 +170,14 @@ private: // for each slice with the latest value. void updateCurrentSlicedBucketForAnomaly(); - // Whitelist of fields to report. Empty means all are reported. + // Allowlist of fields to report. Empty means all are reported. std::vector<Matcher> mFieldMatchers; GaugeMetric::SamplingType mSamplingType; const int64_t mMaxPullDelayNs; - // apply a whitelist on the original input + // apply an allowlist on the original input std::shared_ptr<vector<FieldValue>> getGaugeFields(const LogEvent& event); // Util function to check whether the specified dimension hits the guardrail. diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 2c3deca40fa0..5a520326116a 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -24,8 +24,8 @@ #include "condition/CombinationConditionTracker.h" #include "condition/SimpleConditionTracker.h" #include "guardrail/StatsdStats.h" -#include "matchers/CombinationLogMatchingTracker.h" -#include "matchers/SimpleLogMatchingTracker.h" +#include "matchers/CombinationAtomMatchingTracker.h" +#include "matchers/SimpleAtomMatchingTracker.h" #include "parsing_utils/config_update_utils.h" #include "parsing_utils/metrics_manager_util.h" #include "state/StateManager.h" @@ -77,14 +77,14 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, // Init the ttl end timestamp. refreshTtl(timeBaseNs); - mConfigValid = - initStatsdConfig(key, config, uidMap, pullerManager, anomalyAlarmMonitor, - periodicAlarmMonitor, timeBaseNs, currentTimeNs, mTagIds, - mAllAtomMatchers, mLogTrackerMap, mAllConditionTrackers, - mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers, - mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, - mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap, - mAlertTrackerMap, mMetricIndexesWithActivation, mNoReportMetricIds); + mConfigValid = initStatsdConfig( + key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap, + mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers, + mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap, + mTrackerToConditionMap, mActivationAtomTrackerToMetricMap, + mDeactivationAtomTrackerToMetricMap, mAlertTrackerMap, mMetricIndexesWithActivation, + mNoReportMetricIds); mHashStringsInReport = config.hash_strings_in_metric_report(); mVersionStringsInReport = config.version_strings_in_metric_report(); @@ -93,7 +93,7 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, // Init allowed pushed atom uids. if (config.allowed_log_source_size() == 0) { mConfigValid = false; - ALOGE("Log source whitelist is empty! This config won't get any data. Suggest adding at " + ALOGE("Log source allowlist is empty! This config won't get any data. Suggest adding at " "least AID_SYSTEM and AID_STATSD to the allowed_log_source field."); } else { for (const auto& source : config.allowed_log_source()) { @@ -155,7 +155,7 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, // Guardrail. Reject the config if it's too big. if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig || mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig || - mAllAtomMatchers.size() > StatsdStats::kMaxMatcherCountPerConfig) { + mAllAtomMatchingTrackers.size() > StatsdStats::kMaxMatcherCountPerConfig) { ALOGE("This config is too big! Reject!"); mConfigValid = false; } @@ -175,8 +175,9 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, // no matter whether this config is valid, log it in the stats. StatsdStats::getInstance().noteConfigReceived( - key, mAllMetricProducers.size(), mAllConditionTrackers.size(), mAllAtomMatchers.size(), - mAllAnomalyTrackers.size(), mAnnotations, mConfigValid); + key, mAllMetricProducers.size(), mAllConditionTrackers.size(), + mAllAtomMatchingTrackers.size(), mAllAnomalyTrackers.size(), mAnnotations, + mConfigValid); // Check active for (const auto& metric : mAllMetricProducers) { if (metric->isActive()) { @@ -201,15 +202,15 @@ bool MetricsManager::updateConfig(const StatsdConfig& config, const int64_t time const int64_t currentTimeNs, const sp<AlarmMonitor>& anomalyAlarmMonitor, const sp<AlarmMonitor>& periodicAlarmMonitor) { - vector<sp<LogMatchingTracker>> newAtomMatchers; - unordered_map<int64_t, int> newLogTrackerMap; + vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers; + unordered_map<int64_t, int> newAtomMatchingTrackerMap; mTagIds.clear(); - mConfigValid = - updateStatsdConfig(mConfigKey, config, mUidMap, mPullerManager, anomalyAlarmMonitor, - periodicAlarmMonitor, timeBaseNs, currentTimeNs, mAllAtomMatchers, - mLogTrackerMap, mTagIds, newAtomMatchers, newLogTrackerMap); - mAllAtomMatchers = newAtomMatchers; - mLogTrackerMap = newLogTrackerMap; + mConfigValid = updateStatsdConfig( + mConfigKey, config, mUidMap, mPullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap, mTagIds, + newAtomMatchingTrackers, newAtomMatchingTrackerMap); + mAllAtomMatchingTrackers = newAtomMatchingTrackers; + mAtomMatchingTrackerMap = newAtomMatchingTrackerMap; return mConfigValid; } @@ -502,11 +503,12 @@ void MetricsManager::onLogEvent(const LogEvent& event) { return; } - vector<MatchingState> matcherCache(mAllAtomMatchers.size(), MatchingState::kNotComputed); + vector<MatchingState> matcherCache(mAllAtomMatchingTrackers.size(), + MatchingState::kNotComputed); // Evaluate all atom matchers. - for (auto& matcher : mAllAtomMatchers) { - matcher->onLogEvent(event, mAllAtomMatchers, matcherCache); + for (auto& matcher : mAllAtomMatchingTrackers) { + matcher->onLogEvent(event, mAllAtomMatchingTrackers, matcherCache); } // Set of metrics that received an activation cancellation. @@ -596,10 +598,10 @@ void MetricsManager::onLogEvent(const LogEvent& event) { } // For matched AtomMatchers, tell relevant metrics that a matched event has come. - for (size_t i = 0; i < mAllAtomMatchers.size(); i++) { + for (size_t i = 0; i < mAllAtomMatchingTrackers.size(); i++) { if (matcherCache[i] == MatchingState::kMatched) { StatsdStats::getInstance().noteMatcherMatched(mConfigKey, - mAllAtomMatchers[i]->getId()); + mAllAtomMatchingTrackers[i]->getId()); auto pair = mTrackerToMetricMap.find(i); if (pair != mTrackerToMetricMap.end()) { auto& metricList = pair->second; diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 6f9a23362033..6f4b2d7e9fa4 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -25,7 +25,7 @@ #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" #include "logd/LogEvent.h" -#include "matchers/LogMatchingTracker.h" +#include "matchers/AtomMatchingTracker.h" #include "metrics/MetricProducer.h" #include "packages/UidMap.h" @@ -217,7 +217,7 @@ private: // All event tags that are interesting to my metrics. std::set<int> mTagIds; - // We only store the sp of LogMatchingTracker, MetricProducer, and ConditionTracker in + // We only store the sp of AtomMatchingTracker, MetricProducer, and ConditionTracker in // MetricsManager. There are relationships between them, and the relationships are denoted by // index instead of pointers. The reasons for this are: (1) the relationship between them are // complicated, so storing index instead of pointers reduces the risk that A holds B's sp, and B @@ -225,7 +225,7 @@ private: // the related results from a cache using the index. // Hold all the atom matchers from the config. - std::vector<sp<LogMatchingTracker>> mAllAtomMatchers; + std::vector<sp<AtomMatchingTracker>> mAllAtomMatchingTrackers; // Hold all the conditions from the config. std::vector<sp<ConditionTracker>> mAllConditionTrackers; @@ -239,29 +239,29 @@ private: // Hold all periodic alarm trackers. std::vector<sp<AlarmTracker>> mAllPeriodicAlarmTrackers; - // To make updating configs faster, we map the id of a LogMatchingTracker, MetricProducer, and + // To make updating configs faster, we map the id of a AtomMatchingTracker, MetricProducer, and // ConditionTracker to its index in the corresponding vector. - // Maps the id of an atom matcher to its index in mAllAtomMatchers. - std::unordered_map<int64_t, int> mLogTrackerMap; + // Maps the id of an atom matcher to its index in mAllAtomMatchingTrackers. + std::unordered_map<int64_t, int> mAtomMatchingTrackerMap; // To make the log processing more efficient, we want to do as much filtering as possible // before we go into individual trackers and conditions to match. // 1st filter: check if the event tag id is in mTagIds. // 2nd filter: if it is, we parse the event because there is at least one member is interested. - // then pass to all LogMatchingTrackers (itself also filter events by ids). - // 3nd filter: for LogMatchingTrackers that matched this event, we pass this event to the + // then pass to all AtomMatchingTrackers (itself also filter events by ids). + // 3nd filter: for AtomMatchingTrackers that matched this event, we pass this event to the // ConditionTrackers and MetricProducers that use this matcher. // 4th filter: for ConditionTrackers that changed value due to this event, we pass // new conditions to metrics that use this condition. // The following map is initialized from the statsd_config. - // Maps from the index of the LogMatchingTracker to index of MetricProducer. + // Maps from the index of the AtomMatchingTracker to index of MetricProducer. std::unordered_map<int, std::vector<int>> mTrackerToMetricMap; - // Maps from LogMatchingTracker to ConditionTracker + // Maps from AtomMatchingTracker to ConditionTracker std::unordered_map<int, std::vector<int>> mTrackerToConditionMap; // Maps from ConditionTracker to MetricProducer diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp index 562e29124187..a9ae5a4e854d 100644 --- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp +++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp @@ -29,9 +29,9 @@ namespace statsd { // Recursive function to determine if a matcher needs to be updated. Populates matcherToUpdate. // Returns whether the function was successful or not. bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherIdx, - const unordered_map<int64_t, int>& oldLogTrackerMap, - const vector<sp<LogMatchingTracker>>& oldAtomMatchers, - const unordered_map<int64_t, int>& newLogTrackerMap, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, + const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, vector<UpdateStatus>& matchersToUpdate, vector<bool>& cycleTracker) { // Have already examined this matcher. @@ -42,8 +42,8 @@ bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherI const AtomMatcher& matcher = config.atom_matcher(matcherIdx); int64_t id = matcher.id(); // Check if new matcher. - const auto& oldLogTrackerIt = oldLogTrackerMap.find(id); - if (oldLogTrackerIt == oldLogTrackerMap.end()) { + const auto& oldAtomMatchingTrackerIt = oldAtomMatchingTrackerMap.find(id); + if (oldAtomMatchingTrackerIt == oldAtomMatchingTrackerMap.end()) { matchersToUpdate[matcherIdx] = UPDATE_REPLACE; return true; } @@ -55,7 +55,7 @@ bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherI return false; } uint64_t newProtoHash = Hash64(serializedMatcher); - if (newProtoHash != oldAtomMatchers[oldLogTrackerIt->second]->getProtoHash()) { + if (newProtoHash != oldAtomMatchingTrackers[oldAtomMatchingTrackerIt->second]->getProtoHash()) { matchersToUpdate[matcherIdx] = UPDATE_REPLACE; return true; } @@ -70,8 +70,8 @@ bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherI cycleTracker[matcherIdx] = true; UpdateStatus status = UPDATE_PRESERVE; for (const int64_t childMatcherId : matcher.combination().matcher()) { - const auto& childIt = newLogTrackerMap.find(childMatcherId); - if (childIt == newLogTrackerMap.end()) { + const auto& childIt = newAtomMatchingTrackerMap.find(childMatcherId); + if (childIt == newAtomMatchingTrackerMap.end()) { ALOGW("Matcher %lld not found in the config", (long long)childMatcherId); return false; } @@ -80,9 +80,9 @@ bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherI ALOGE("Cycle detected in matcher config"); return false; } - if (!determineMatcherUpdateStatus(config, childIdx, oldLogTrackerMap, - oldAtomMatchers, newLogTrackerMap, - matchersToUpdate, cycleTracker)) { + if (!determineMatcherUpdateStatus( + config, childIdx, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, + newAtomMatchingTrackerMap, matchersToUpdate, cycleTracker)) { return false; } @@ -103,25 +103,25 @@ bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherI return true; } -bool updateLogTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, - const unordered_map<int64_t, int>& oldLogTrackerMap, - const vector<sp<LogMatchingTracker>>& oldAtomMatchers, set<int>& allTagIds, - unordered_map<int64_t, int>& newLogTrackerMap, - vector<sp<LogMatchingTracker>>& newAtomMatchers) { +bool updateAtomTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, + set<int>& allTagIds, unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers) { const int atomMatcherCount = config.atom_matcher_size(); vector<AtomMatcher> matcherProtos; matcherProtos.reserve(atomMatcherCount); - newAtomMatchers.reserve(atomMatcherCount); + newAtomMatchingTrackers.reserve(atomMatcherCount); // Maps matcher id to their position in the config. For fast lookup of dependencies. for (int i = 0; i < atomMatcherCount; i++) { const AtomMatcher& matcher = config.atom_matcher(i); - if (newLogTrackerMap.find(matcher.id()) != newLogTrackerMap.end()) { + if (newAtomMatchingTrackerMap.find(matcher.id()) != newAtomMatchingTrackerMap.end()) { ALOGE("Duplicate atom matcher found for id %lld", (long long)matcher.id()); return false; } - newLogTrackerMap[matcher.id()] = i; + newAtomMatchingTrackerMap[matcher.id()] = i; matcherProtos.push_back(matcher); } @@ -129,8 +129,9 @@ bool updateLogTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, vector<UpdateStatus> matchersToUpdate(atomMatcherCount, UPDATE_UNKNOWN); vector<bool> cycleTracker(atomMatcherCount, false); for (int i = 0; i < atomMatcherCount; i++) { - if (!determineMatcherUpdateStatus(config, i, oldLogTrackerMap, oldAtomMatchers, - newLogTrackerMap, matchersToUpdate, cycleTracker)) { + if (!determineMatcherUpdateStatus(config, i, oldAtomMatchingTrackerMap, + oldAtomMatchingTrackers, newAtomMatchingTrackerMap, + matchersToUpdate, cycleTracker)) { return false; } } @@ -140,23 +141,23 @@ bool updateLogTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, const int64_t id = matcher.id(); switch (matchersToUpdate[i]) { case UPDATE_PRESERVE: { - const auto& oldLogTrackerIt = oldLogTrackerMap.find(id); - if (oldLogTrackerIt == oldLogTrackerMap.end()) { + const auto& oldAtomMatchingTrackerIt = oldAtomMatchingTrackerMap.find(id); + if (oldAtomMatchingTrackerIt == oldAtomMatchingTrackerMap.end()) { ALOGE("Could not find AtomMatcher %lld in the previous config, but expected it " "to be there", (long long)id); return false; } - const int oldIndex = oldLogTrackerIt->second; - newAtomMatchers.push_back(oldAtomMatchers[oldIndex]); + const int oldIndex = oldAtomMatchingTrackerIt->second; + newAtomMatchingTrackers.push_back(oldAtomMatchingTrackers[oldIndex]); break; } case UPDATE_REPLACE: { - sp<LogMatchingTracker> tracker = createLogTracker(matcher, i, uidMap); + sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, i, uidMap); if (tracker == nullptr) { return false; } - newAtomMatchers.push_back(tracker); + newAtomMatchingTrackers.push_back(tracker); break; } default: { @@ -168,8 +169,9 @@ bool updateLogTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, } std::fill(cycleTracker.begin(), cycleTracker.end(), false); - for (auto& matcher : newAtomMatchers) { - if (!matcher->init(matcherProtos, newAtomMatchers, newLogTrackerMap, cycleTracker)) { + for (auto& matcher : newAtomMatchingTrackers) { + if (!matcher->init(matcherProtos, newAtomMatchingTrackers, newAtomMatchingTrackerMap, + cycleTracker)) { return false; } // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only. @@ -185,13 +187,14 @@ bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const const sp<AlarmMonitor>& anomalyAlarmMonitor, const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, const int64_t currentTimeNs, - const vector<sp<LogMatchingTracker>>& oldAtomMatchers, - const unordered_map<int64_t, int>& oldLogTrackerMap, set<int>& allTagIds, - vector<sp<LogMatchingTracker>>& newAtomMatchers, - unordered_map<int64_t, int>& newLogTrackerMap) { - if (!updateLogTrackers(config, uidMap, oldLogTrackerMap, oldAtomMatchers, allTagIds, - newLogTrackerMap, newAtomMatchers)) { - ALOGE("updateLogMatchingTrackers failed"); + const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + set<int>& allTagIds, + vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, + unordered_map<int64_t, int>& newAtomMatchingTrackerMap) { + if (!updateAtomTrackers(config, uidMap, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, + allTagIds, newAtomMatchingTrackerMap, newAtomMatchingTrackers)) { + ALOGE("updateAtomMatchingTrackers failed"); return false; } @@ -200,4 +203,4 @@ bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h index 951ab03cee47..ae7b2162e034 100644 --- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h +++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h @@ -20,7 +20,7 @@ #include "anomaly/AlarmMonitor.h" #include "external/StatsPullerManager.h" -#include "matchers/LogMatchingTracker.h" +#include "matchers/AtomMatchingTracker.h" namespace android { namespace os { @@ -42,34 +42,34 @@ enum UpdateStatus { // input: // [config]: the input StatsdConfig // [matcherIdx]: the index of the current matcher to be updated -// [newLogTrackerMap]: matcher id to index mapping in the input StatsdConfig -// [oldLogTrackerMap]: matcher id to index mapping in the existing MetricsManager -// [oldAtomMatchers]: stores the existing LogMatchingTrackers +// [newAtomMatchingTrackerMap]: matcher id to index mapping in the input StatsdConfig +// [oldAtomMatchingTrackerMap]: matcher id to index mapping in the existing MetricsManager +// [oldAtomMatchingTrackers]: stores the existing AtomMatchingTrackers // output: // [matchersToUpdate]: vector of the update status of each matcher. The matcherIdx index will // be updated from UPDATE_UNKNOWN after this call. // [cycleTracker]: intermediate param used during recursion. bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherIdx, - const unordered_map<int64_t, int>& oldLogTrackerMap, - const vector<sp<LogMatchingTracker>>& oldAtomMatchers, - const unordered_map<int64_t, int>& newLogTrackerMap, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, + const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, vector<UpdateStatus>& matchersToUpdate, vector<bool>& cycleTracker); -// Updates the LogMatchingTrackers. +// Updates the AtomMatchingTrackers. // input: // [config]: the input StatsdConfig -// [oldLogTrackerMap]: existing matcher id to index mapping -// [oldAtomMatchers]: stores the existing LogMatchingTrackers +// [oldAtomMatchingTrackerMap]: existing matcher id to index mapping +// [oldAtomMatchingTrackers]: stores the existing AtomMatchingTrackers // output: // [allTagIds]: contains the set of all interesting tag ids to this config. -// [newLogTrackerMap]: new matcher id to index mapping -// [newAtomMatchers]: stores the new LogMatchingTrackers -bool updateLogTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, - const unordered_map<int64_t, int>& oldLogTrackerMap, - const vector<sp<LogMatchingTracker>>& oldAtomMatchers, set<int>& allTagIds, - unordered_map<int64_t, int>& newLogTrackerMap, - vector<sp<LogMatchingTracker>>& newAtomMatchers); +// [newAtomMatchingTrackerMap]: new matcher id to index mapping +// [newAtomMatchers]: stores the new AtomMatchingTrackers +bool updateAtomTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, + set<int>& allTagIds, unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers); // Updates the existing MetricsManager from a new StatsdConfig. // Parameters are the members of MetricsManager. See MetricsManager for declaration. @@ -78,12 +78,12 @@ bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const const sp<AlarmMonitor>& anomalyAlarmMonitor, const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, const int64_t currentTimeNs, - const std::vector<sp<LogMatchingTracker>>& oldAtomMatchers, - const unordered_map<int64_t, int>& oldLogTrackerMap, + const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, std::set<int>& allTagIds, - std::vector<sp<LogMatchingTracker>>& newAtomMatchers, - unordered_map<int64_t, int>& newLogTrackerMap); + std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers, + unordered_map<int64_t, int>& newAtomMatchingTrackerMap); } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp index 52ef95d19cdc..daf67e93557c 100644 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp @@ -26,9 +26,9 @@ #include "condition/SimpleConditionTracker.h" #include "external/StatsPullerManager.h" #include "hash.h" -#include "matchers/CombinationLogMatchingTracker.h" +#include "matchers/CombinationAtomMatchingTracker.h" #include "matchers/EventMatcherWizard.h" -#include "matchers/SimpleLogMatchingTracker.h" +#include "matchers/SimpleAtomMatchingTracker.h" #include "metrics/CountMetricProducer.h" #include "metrics/DurationMetricProducer.h" #include "metrics/EventMetricProducer.h" @@ -62,8 +62,8 @@ bool hasLeafNode(const FieldMatcher& matcher) { } // namespace -sp<LogMatchingTracker> createLogTracker(const AtomMatcher& logMatcher, const int index, - const sp<UidMap>& uidMap) { +sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index, + const sp<UidMap>& uidMap) { string serializedMatcher; if (!logMatcher.SerializeToString(&serializedMatcher)) { ALOGE("Unable to serialize matcher %lld", (long long)logMatcher.id()); @@ -72,11 +72,11 @@ sp<LogMatchingTracker> createLogTracker(const AtomMatcher& logMatcher, const int uint64_t protoHash = Hash64(serializedMatcher); switch (logMatcher.contents_case()) { case AtomMatcher::ContentsCase::kSimpleAtomMatcher: - return new SimpleLogMatchingTracker(logMatcher.id(), index, protoHash, - logMatcher.simple_atom_matcher(), uidMap); + return new SimpleAtomMatchingTracker(logMatcher.id(), index, protoHash, + logMatcher.simple_atom_matcher(), uidMap); break; case AtomMatcher::ContentsCase::kCombination: - return new CombinationLogMatchingTracker(logMatcher.id(), index, protoHash); + return new CombinationAtomMatchingTracker(logMatcher.id(), index, protoHash); break; default: ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id()); @@ -84,18 +84,18 @@ sp<LogMatchingTracker> createLogTracker(const AtomMatcher& logMatcher, const int } } -bool handleMetricWithLogTrackers(const int64_t what, const int metricIndex, - const bool usedForDimension, - const vector<sp<LogMatchingTracker>>& allAtomMatchers, - const unordered_map<int64_t, int>& logTrackerMap, - unordered_map<int, std::vector<int>>& trackerToMetricMap, - int& logTrackerIndex) { - auto logTrackerIt = logTrackerMap.find(what); - if (logTrackerIt == logTrackerMap.end()) { +bool handleMetricWithAtomMatchingTrackers( + const int64_t what, const int metricIndex, const bool usedForDimension, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const unordered_map<int64_t, int>& atomMatchingTrackerMap, + unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex) { + auto logTrackerIt = atomMatchingTrackerMap.find(what); + if (logTrackerIt == atomMatchingTrackerMap.end()) { ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)what); return false; } - if (usedForDimension && allAtomMatchers[logTrackerIt->second]->getAtomIds().size() > 1) { + if (usedForDimension && + allAtomMatchingTrackers[logTrackerIt->second]->getAtomIds().size() > 1) { ALOGE("AtomMatcher \"%lld\" has more than one tag ids. When a metric has dimension, " "the \"what\" can only about one atom type.", (long long)what); @@ -107,17 +107,17 @@ bool handleMetricWithLogTrackers(const int64_t what, const int metricIndex, return true; } -bool handlePullMetricTriggerWithLogTrackers( +bool handlePullMetricTriggerWithAtomMatchingTrackers( const int64_t trigger, const int metricIndex, - const vector<sp<LogMatchingTracker>>& allAtomMatchers, - const unordered_map<int64_t, int>& logTrackerMap, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const unordered_map<int64_t, int>& atomMatchingTrackerMap, unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex) { - auto logTrackerIt = logTrackerMap.find(trigger); - if (logTrackerIt == logTrackerMap.end()) { + auto logTrackerIt = atomMatchingTrackerMap.find(trigger); + if (logTrackerIt == atomMatchingTrackerMap.end()) { ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)trigger); return false; } - if (allAtomMatchers[logTrackerIt->second]->getAtomIds().size() > 1) { + if (allAtomMatchingTrackers[logTrackerIt->second]->getAtomIds().size() > 1) { ALOGE("AtomMatcher \"%lld\" has more than one tag ids." "Trigger can only be one atom type.", (long long)trigger); @@ -148,8 +148,6 @@ bool handleMetricWithConditions( ALOGW("cannot find Predicate \"%lld\" in the config", (long long)link.condition()); return false; } - allConditionTrackers[condition_it->second]->setSliced(true); - allConditionTrackers[it->second]->setSliced(true); } conditionIndex = condition_it->second; @@ -209,7 +207,7 @@ bool handleMetricWithStateLink(const FieldMatcher& stateMatcher, bool handleMetricActivation( const StatsdConfig& config, const int64_t metricId, const int metricIndex, const unordered_map<int64_t, int>& metricToActivationMap, - const unordered_map<int64_t, int>& logTrackerMap, + const unordered_map<int64_t, int>& atomMatchingTrackerMap, unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, vector<int>& metricsWithActivation, @@ -225,8 +223,8 @@ bool handleMetricActivation( for (int i = 0; i < metricActivation.event_activation_size(); i++) { const EventActivation& activation = metricActivation.event_activation(i); - auto itr = logTrackerMap.find(activation.atom_matcher_id()); - if (itr == logTrackerMap.end()) { + auto itr = atomMatchingTrackerMap.find(activation.atom_matcher_id()); + if (itr == atomMatchingTrackerMap.end()) { ALOGE("Atom matcher not found for event activation."); return false; } @@ -242,8 +240,8 @@ bool handleMetricActivation( eventActivationMap.emplace(atomMatcherIndex, activationWrapper); if (activation.has_deactivation_atom_matcher_id()) { - itr = logTrackerMap.find(activation.deactivation_atom_matcher_id()); - if (itr == logTrackerMap.end()) { + itr = atomMatchingTrackerMap.find(activation.deactivation_atom_matcher_id()); + if (itr == atomMatchingTrackerMap.end()) { ALOGE("Atom matcher not found for event deactivation."); return false; } @@ -257,33 +255,35 @@ bool handleMetricActivation( return true; } -bool initLogTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, - unordered_map<int64_t, int>& logTrackerMap, - vector<sp<LogMatchingTracker>>& allAtomMatchers, set<int>& allTagIds) { +bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, + unordered_map<int64_t, int>& atomMatchingTrackerMap, + vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + set<int>& allTagIds) { vector<AtomMatcher> matcherConfigs; const int atomMatcherCount = config.atom_matcher_size(); matcherConfigs.reserve(atomMatcherCount); - allAtomMatchers.reserve(atomMatcherCount); + allAtomMatchingTrackers.reserve(atomMatcherCount); for (int i = 0; i < atomMatcherCount; i++) { const AtomMatcher& logMatcher = config.atom_matcher(i); - int index = allAtomMatchers.size(); - sp<LogMatchingTracker> tracker = createLogTracker(logMatcher, index, uidMap); + int index = allAtomMatchingTrackers.size(); + sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(logMatcher, index, uidMap); if (tracker == nullptr) { return false; } - allAtomMatchers.push_back(tracker); - if (logTrackerMap.find(logMatcher.id()) != logTrackerMap.end()) { + allAtomMatchingTrackers.push_back(tracker); + if (atomMatchingTrackerMap.find(logMatcher.id()) != atomMatchingTrackerMap.end()) { ALOGE("Duplicate AtomMatcher found!"); return false; } - logTrackerMap[logMatcher.id()] = index; + atomMatchingTrackerMap[logMatcher.id()] = index; matcherConfigs.push_back(logMatcher); } - vector<bool> stackTracker2(allAtomMatchers.size(), false); - for (auto& matcher : allAtomMatchers) { - if (!matcher->init(matcherConfigs, allAtomMatchers, logTrackerMap, stackTracker2)) { + vector<bool> stackTracker2(allAtomMatchingTrackers.size(), false); + for (auto& matcher : allAtomMatchingTrackers) { + if (!matcher->init(matcherConfigs, allAtomMatchingTrackers, atomMatchingTrackerMap, + stackTracker2)) { return false; } // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only. @@ -294,7 +294,7 @@ bool initLogTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, } bool initConditions(const ConfigKey& key, const StatsdConfig& config, - const unordered_map<int64_t, int>& logTrackerMap, + const unordered_map<int64_t, int>& atomMatchingTrackerMap, unordered_map<int64_t, int>& conditionTrackerMap, vector<sp<ConditionTracker>>& allConditionTrackers, unordered_map<int, std::vector<int>>& trackerToConditionMap, @@ -312,7 +312,8 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, switch (condition.contents_case()) { case Predicate::ContentsCase::kSimplePredicate: { allConditionTrackers.push_back(new SimpleConditionTracker( - key, condition.id(), index, condition.simple_predicate(), logTrackerMap)); + key, condition.id(), index, condition.simple_predicate(), + atomMatchingTrackerMap)); break; } case Predicate::ContentsCase::kCombination: { @@ -339,7 +340,7 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, stackTracker, initialConditionCache)) { return false; } - for (const int trackerIndex : conditionTracker->getLogTrackerIndex()) { + for (const int trackerIndex : conditionTracker->getAtomMatchingTrackerIndex()) { auto& conditionList = trackerToConditionMap[trackerIndex]; conditionList.push_back(i); } @@ -367,9 +368,9 @@ bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAt bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs, const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, - const unordered_map<int64_t, int>& logTrackerMap, + const unordered_map<int64_t, int>& atomMatchingTrackerMap, const unordered_map<int64_t, int>& conditionTrackerMap, - const vector<sp<LogMatchingTracker>>& allAtomMatchers, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const unordered_map<int64_t, int>& stateAtomIdMap, const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, vector<sp<ConditionTracker>>& allConditionTrackers, @@ -382,7 +383,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, vector<int>& metricsWithActivation) { sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers); - sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchers); + sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchingTrackers); const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() + config.event_metric_size() + config.gauge_metric_size() + config.value_metric_size(); @@ -413,9 +414,10 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t int metricIndex = allMetricProducers.size(); metricMap.insert({metric.id(), metricIndex}); int trackerIndex; - if (!handleMetricWithLogTrackers(metric.what(), metricIndex, - metric.has_dimensions_in_what(), allAtomMatchers, - logTrackerMap, trackerToMetricMap, trackerIndex)) { + if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, + metric.has_dimensions_in_what(), + allAtomMatchingTrackers, atomMatchingTrackerMap, + trackerToMetricMap, trackerIndex)) { return false; } @@ -450,7 +452,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; bool success = handleMetricActivation( - config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap, + config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap, eventDeactivationMap); if (!success) return false; @@ -487,24 +489,27 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t int trackerIndices[3] = {-1, -1, -1}; if (!simplePredicate.has_start() || - !handleMetricWithLogTrackers(simplePredicate.start(), metricIndex, - metric.has_dimensions_in_what(), allAtomMatchers, - logTrackerMap, trackerToMetricMap, trackerIndices[0])) { + !handleMetricWithAtomMatchingTrackers(simplePredicate.start(), metricIndex, + metric.has_dimensions_in_what(), + allAtomMatchingTrackers, atomMatchingTrackerMap, + trackerToMetricMap, trackerIndices[0])) { ALOGE("Duration metrics must specify a valid the start event matcher"); return false; } if (simplePredicate.has_stop() && - !handleMetricWithLogTrackers(simplePredicate.stop(), metricIndex, - metric.has_dimensions_in_what(), allAtomMatchers, - logTrackerMap, trackerToMetricMap, trackerIndices[1])) { + !handleMetricWithAtomMatchingTrackers(simplePredicate.stop(), metricIndex, + metric.has_dimensions_in_what(), + allAtomMatchingTrackers, atomMatchingTrackerMap, + trackerToMetricMap, trackerIndices[1])) { return false; } if (simplePredicate.has_stop_all() && - !handleMetricWithLogTrackers(simplePredicate.stop_all(), metricIndex, - metric.has_dimensions_in_what(), allAtomMatchers, - logTrackerMap, trackerToMetricMap, trackerIndices[2])) { + !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex, + metric.has_dimensions_in_what(), + allAtomMatchingTrackers, atomMatchingTrackerMap, + trackerToMetricMap, trackerIndices[2])) { return false; } @@ -556,7 +561,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; bool success = handleMetricActivation( - config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap, + config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap, eventDeactivationMap); if (!success) return false; @@ -580,8 +585,9 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t return false; } int trackerIndex; - if (!handleMetricWithLogTrackers(metric.what(), metricIndex, false, allAtomMatchers, - logTrackerMap, trackerToMetricMap, trackerIndex)) { + if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false, + allAtomMatchingTrackers, atomMatchingTrackerMap, + trackerToMetricMap, trackerIndex)) { return false; } @@ -603,7 +609,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; bool success = handleMetricActivation( - config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap, + config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap, eventDeactivationMap); if (!success) return false; @@ -636,13 +642,14 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t int metricIndex = allMetricProducers.size(); metricMap.insert({metric.id(), metricIndex}); int trackerIndex; - if (!handleMetricWithLogTrackers(metric.what(), metricIndex, - metric.has_dimensions_in_what(), allAtomMatchers, - logTrackerMap, trackerToMetricMap, trackerIndex)) { + if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, + metric.has_dimensions_in_what(), + allAtomMatchingTrackers, atomMatchingTrackerMap, + trackerToMetricMap, trackerIndex)) { return false; } - sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex); + sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex); // If it is pulled atom, it should be simple matcher with one tagId. if (atomMatcher->getAtomIds().size() != 1) { return false; @@ -691,7 +698,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; bool success = handleMetricActivation( - config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap, + config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap, eventDeactivationMap); if (!success) return false; @@ -727,13 +734,14 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t int metricIndex = allMetricProducers.size(); metricMap.insert({metric.id(), metricIndex}); int trackerIndex; - if (!handleMetricWithLogTrackers(metric.what(), metricIndex, - metric.has_dimensions_in_what(), allAtomMatchers, - logTrackerMap, trackerToMetricMap, trackerIndex)) { + if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, + metric.has_dimensions_in_what(), + allAtomMatchingTrackers, atomMatchingTrackerMap, + trackerToMetricMap, trackerIndex)) { return false; } - sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex); + sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex); // For GaugeMetric atom, it should be simple matcher with one tagId. if (atomMatcher->getAtomIds().size() != 1) { return false; @@ -752,12 +760,13 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) { return false; } - if (!handlePullMetricTriggerWithLogTrackers(metric.trigger_event(), metricIndex, - allAtomMatchers, logTrackerMap, - trackerToMetricMap, triggerTrackerIndex)) { + if (!handlePullMetricTriggerWithAtomMatchingTrackers( + metric.trigger_event(), metricIndex, allAtomMatchingTrackers, + atomMatchingTrackerMap, trackerToMetricMap, triggerTrackerIndex)) { return false; } - sp<LogMatchingTracker> triggerAtomMatcher = allAtomMatchers.at(triggerTrackerIndex); + sp<AtomMatchingTracker> triggerAtomMatcher = + allAtomMatchingTrackers.at(triggerTrackerIndex); triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin()); } @@ -785,7 +794,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; bool success = handleMetricActivation( - config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap, + config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap, eventDeactivationMap); if (!success) return false; @@ -923,8 +932,8 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp const sp<AlarmMonitor>& anomalyAlarmMonitor, const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, const int64_t currentTimeNs, set<int>& allTagIds, - vector<sp<LogMatchingTracker>>& allAtomMatchers, - unordered_map<int64_t, int>& logTrackerMap, + vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + unordered_map<int64_t, int>& atomMatchingTrackerMap, vector<sp<ConditionTracker>>& allConditionTrackers, vector<sp<MetricProducer>>& allMetricProducers, vector<sp<AnomalyTracker>>& allAnomalyTrackers, @@ -942,14 +951,15 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp unordered_map<int64_t, int> stateAtomIdMap; unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; - if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) { - ALOGE("initLogMatchingTrackers failed"); + if (!initAtomMatchingTrackers(config, uidMap, atomMatchingTrackerMap, allAtomMatchingTrackers, + allTagIds)) { + ALOGE("initAtomMatchingTrackers failed"); return false; } - VLOG("initLogMatchingTrackers succeed..."); + VLOG("initAtomMatchingTrackers succeed..."); - if (!initConditions(key, config, logTrackerMap, conditionTrackerMap, allConditionTrackers, - trackerToConditionMap, initialConditionCache)) { + if (!initConditions(key, config, atomMatchingTrackerMap, conditionTrackerMap, + allConditionTrackers, trackerToConditionMap, initialConditionCache)) { ALOGE("initConditionTrackers failed"); return false; } @@ -958,12 +968,12 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp ALOGE("initStates failed"); return false; } - if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, logTrackerMap, - conditionTrackerMap, allAtomMatchers, stateAtomIdMap, allStateGroupMaps, - allConditionTrackers, initialConditionCache, allMetricProducers, - conditionToMetricMap, trackerToMetricMap, metricProducerMap, noReportMetricIds, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation)) { + if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, atomMatchingTrackerMap, + conditionTrackerMap, allAtomMatchingTrackers, stateAtomIdMap, + allStateGroupMaps, allConditionTrackers, initialConditionCache, + allMetricProducers, conditionToMetricMap, trackerToMetricMap, + metricProducerMap, noReportMetricIds, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation)) { ALOGE("initMetricProducers failed"); return false; } diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h index ed9951fd5ee6..4cfd1b0465ea 100644 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h @@ -23,7 +23,7 @@ #include "anomaly/AlarmTracker.h" #include "condition/ConditionTracker.h" #include "external/StatsPullerManager.h" -#include "matchers/LogMatchingTracker.h" +#include "matchers/AtomMatchingTracker.h" #include "metrics/MetricProducer.h" namespace android { @@ -33,14 +33,14 @@ namespace statsd { // Helper functions for creating individual config components from StatsdConfig. // Should only be called from metrics_manager_util and config_update_utils. -// Create a LogMatchingTracker. +// Create a AtomMatchingTracker. // input: // [logMatcher]: the input AtomMatcher from the StatsdConfig // [index]: the index of the matcher // output: -// new LogMatchingTracker, or null if the tracker is unable to be created -sp<LogMatchingTracker> createLogTracker(const AtomMatcher& logMatcher, const int index, - const sp<UidMap>& uidMap); +// new AtomMatchingTracker, or null if the tracker is unable to be created +sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index, + const sp<UidMap>& uidMap); // Helper functions for MetricsManager to initialize from StatsdConfig. // *Note*: only initStatsdConfig() should be called from outside. @@ -48,24 +48,24 @@ sp<LogMatchingTracker> createLogTracker(const AtomMatcher& logMatcher, const int // steps, created to make unit tests easier. And most of the parameters in these // functions are temporary objects in the initialization phase. -// Initialize the LogMatchingTrackers. +// Initialize the AtomMatchingTrackers. // input: // [key]: the config key that this config belongs to // [config]: the input StatsdConfig // output: -// [logTrackerMap]: this map should contain matcher name to index mapping -// [allAtomMatchers]: should store the sp to all the LogMatchingTracker +// [atomMatchingTrackerMap]: this map should contain matcher name to index mapping +// [allAtomMatchingTrackers]: should store the sp to all the AtomMatchingTracker // [allTagIds]: contains the set of all interesting tag ids to this config. -bool initLogTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, - std::unordered_map<int64_t, int>& logTrackerMap, - std::vector<sp<LogMatchingTracker>>& allAtomMatchers, - std::set<int>& allTagIds); +bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, + std::unordered_map<int64_t, int>& atomMatchingTrackerMap, + std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + std::set<int>& allTagIds); // Initialize ConditionTrackers // input: // [key]: the config key that this config belongs to // [config]: the input config -// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step. +// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step. // output: // [conditionTrackerMap]: this map should contain condition name to index mapping // [allConditionTrackers]: stores the sp to all the ConditionTrackers @@ -73,7 +73,7 @@ bool initLogTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap, // log tracker to condition trackers that use the log tracker // [initialConditionCache]: stores the initial conditions for each ConditionTracker bool initConditions(const ConfigKey& key, const StatsdConfig& config, - const std::unordered_map<int64_t, int>& logTrackerMap, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, std::unordered_map<int64_t, int>& conditionTrackerMap, std::vector<sp<ConditionTracker>>& allConditionTrackers, std::unordered_map<int, std::vector<int>>& trackerToConditionMap, @@ -96,7 +96,7 @@ bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAt // [key]: the config key that this config belongs to // [config]: the input config // [timeBaseSec]: start time base for all metrics -// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step. +// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step. // [conditionTrackerMap]: condition name to index mapping // [stateAtomIdMap]: contains the mapping from state ids to atom ids // [allStateGroupMaps]: contains the mapping from atom ids and state values to @@ -109,10 +109,10 @@ bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAt bool initMetrics( const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs, const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager, - const std::unordered_map<int64_t, int>& logTrackerMap, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, const std::unordered_map<int64_t, int>& conditionTrackerMap, const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks, - const vector<sp<LogMatchingTracker>>& allAtomMatchers, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, const unordered_map<int64_t, int>& stateAtomIdMap, const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, vector<sp<ConditionTracker>>& allConditionTrackers, @@ -132,8 +132,8 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp const sp<AlarmMonitor>& anomalyAlarmMonitor, const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, const int64_t currentTimeNs, std::set<int>& allTagIds, - std::vector<sp<LogMatchingTracker>>& allAtomMatchers, - std::unordered_map<int64_t, int>& logTrackerMap, + std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + std::unordered_map<int64_t, int>& atomMatchingTrackerMap, std::vector<sp<ConditionTracker>>& allConditionTrackers, std::vector<sp<MetricProducer>>& allMetricProducers, vector<sp<AnomalyTracker>>& allAnomalyTrackers, diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h index eb65dc6979c5..10e065e4113f 100644 --- a/cmds/statsd/src/stats_log_util.h +++ b/cmds/statsd/src/stats_log_util.h @@ -97,7 +97,7 @@ bool parseProtoOutputStream(ProtoOutputStream& protoOutput, T* message) { return message->ParseFromArray(pbBytes.c_str(), pbBytes.size()); } -// Checks the truncate timestamp annotation as well as the blacklisted range of 300,000 - 304,999. +// Checks the truncate timestamp annotation as well as the restricted range of 300,000 - 304,999. // Returns the truncated timestamp to the nearest 5 minutes if needed. int64_t truncateTimestampIfNecessary(const LogEvent& event); diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp index 8dd608347064..2dd774e7dfc9 100644 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -23,7 +23,7 @@ #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "metrics/metrics_test_helper.h" #include "src/condition/ConditionTracker.h" -#include "src/matchers/LogMatchingTracker.h" +#include "src/matchers/AtomMatchingTracker.h" #include "src/metrics/CountMetricProducer.h" #include "src/metrics/GaugeMetricProducer.h" #include "src/metrics/MetricProducer.h" diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 1e6680c47567..1409b621fdf6 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -68,7 +68,7 @@ TEST(StatsLogProcessorTest, TestRateLimitByteSize) { sp<StatsPullerManager> pullerManager = new StatsPullerManager(); sp<AlarmMonitor> anomalyAlarmMonitor; sp<AlarmMonitor> periodicAlarmMonitor; - // Construct the processor with a dummy sendBroadcast function that does nothing. + // Construct the processor with a no-op sendBroadcast function that does nothing. StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, 0, [](const ConfigKey& key) { return true; }, [](const int&, const vector<int64_t>&) {return true;}); @@ -896,8 +896,8 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) { EXPECT_TRUE(metricProducer2->isActive()); int i = 0; - for (; i < metricsManager1->mAllAtomMatchers.size(); i++) { - if (metricsManager1->mAllAtomMatchers[i]->getId() == + for (; i < metricsManager1->mAllAtomMatchingTrackers.size(); i++) { + if (metricsManager1->mAllAtomMatchingTrackers[i]->getId() == metric1ActivationTrigger1->atom_matcher_id()) { break; } @@ -908,8 +908,8 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) { EXPECT_EQ(kNotActive, activation1->state); i = 0; - for (; i < metricsManager1->mAllAtomMatchers.size(); i++) { - if (metricsManager1->mAllAtomMatchers[i]->getId() == + for (; i < metricsManager1->mAllAtomMatchingTrackers.size(); i++) { + if (metricsManager1->mAllAtomMatchingTrackers[i]->getId() == metric1ActivationTrigger2->atom_matcher_id()) { break; } @@ -981,8 +981,8 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) { EXPECT_TRUE(metricProducer1002->isActive()); i = 0; - for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) { - if (metricsManager1001->mAllAtomMatchers[i]->getId() == + for (; i < metricsManager1001->mAllAtomMatchingTrackers.size(); i++) { + if (metricsManager1001->mAllAtomMatchingTrackers[i]->getId() == metric1ActivationTrigger1->atom_matcher_id()) { break; } @@ -993,8 +993,8 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) { EXPECT_EQ(kNotActive, activation1001_1->state); i = 0; - for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) { - if (metricsManager1001->mAllAtomMatchers[i]->getId() == + for (; i < metricsManager1001->mAllAtomMatchingTrackers.size(); i++) { + if (metricsManager1001->mAllAtomMatchingTrackers[i]->getId() == metric1ActivationTrigger2->atom_matcher_id()) { break; } @@ -1082,8 +1082,8 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) { EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); i = 0; - for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) { - if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() == + for (; i < metricsManagerTimeBase3->mAllAtomMatchingTrackers.size(); i++) { + if (metricsManagerTimeBase3->mAllAtomMatchingTrackers[i]->getId() == metric1ActivationTrigger1->atom_matcher_id()) { break; } @@ -1094,8 +1094,8 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) { EXPECT_EQ(kNotActive, activationTimeBase3_1->state); i = 0; - for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) { - if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() == + for (; i < metricsManagerTimeBase3->mAllAtomMatchingTrackers.size(); i++) { + if (metricsManagerTimeBase3->mAllAtomMatchingTrackers[i]->getId() == metric1ActivationTrigger2->atom_matcher_id()) { break; } @@ -1184,8 +1184,8 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) { EXPECT_TRUE(metricProducerTimeBase4_2->isActive()); i = 0; - for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) { - if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() == + for (; i < metricsManagerTimeBase4->mAllAtomMatchingTrackers.size(); i++) { + if (metricsManagerTimeBase4->mAllAtomMatchingTrackers[i]->getId() == metric1ActivationTrigger1->atom_matcher_id()) { break; } @@ -1196,8 +1196,8 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) { EXPECT_EQ(kNotActive, activationTimeBase4_1->state); i = 0; - for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) { - if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() == + for (; i < metricsManagerTimeBase4->mAllAtomMatchingTrackers.size(); i++) { + if (metricsManagerTimeBase4->mAllAtomMatchingTrackers[i]->getId() == metric1ActivationTrigger2->atom_matcher_id()) { break; } @@ -1585,8 +1585,8 @@ TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { EXPECT_TRUE(metricProducer3->isActive()); // Check event activations. - ASSERT_EQ(metricsManager1->mAllAtomMatchers.size(), 4); - EXPECT_EQ(metricsManager1->mAllAtomMatchers[0]->getId(), + ASSERT_EQ(metricsManager1->mAllAtomMatchingTrackers.size(), 4); + EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[0]->getId(), metric1ActivationTrigger1->atom_matcher_id()); const auto& activation1 = metricProducer1->mEventActivationMap.at(0); EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); @@ -1594,7 +1594,7 @@ TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { EXPECT_EQ(kNotActive, activation1->state); EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType); - EXPECT_EQ(metricsManager1->mAllAtomMatchers[1]->getId(), + EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[1]->getId(), metric1ActivationTrigger2->atom_matcher_id()); const auto& activation2 = metricProducer1->mEventActivationMap.at(1); EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns); @@ -1602,7 +1602,7 @@ TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { EXPECT_EQ(kNotActive, activation2->state); EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType); - EXPECT_EQ(metricsManager1->mAllAtomMatchers[2]->getId(), + EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[2]->getId(), metric2ActivationTrigger1->atom_matcher_id()); const auto& activation3 = metricProducer2->mEventActivationMap.at(2); EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns); @@ -1610,7 +1610,7 @@ TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { EXPECT_EQ(kNotActive, activation3->state); EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType); - EXPECT_EQ(metricsManager1->mAllAtomMatchers[3]->getId(), + EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[3]->getId(), metric2ActivationTrigger2->atom_matcher_id()); const auto& activation4 = metricProducer2->mEventActivationMap.at(3); EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns); @@ -1685,8 +1685,8 @@ TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { // Activation 1 is kActiveOnBoot. // Activation 2 and 3 are not active. // Activation 4 is active. - ASSERT_EQ(metricsManager2->mAllAtomMatchers.size(), 4); - EXPECT_EQ(metricsManager2->mAllAtomMatchers[0]->getId(), + ASSERT_EQ(metricsManager2->mAllAtomMatchingTrackers.size(), 4); + EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[0]->getId(), metric1ActivationTrigger1->atom_matcher_id()); const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0); EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns); @@ -1694,7 +1694,7 @@ TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { EXPECT_EQ(kActiveOnBoot, activation1001->state); EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType); - EXPECT_EQ(metricsManager2->mAllAtomMatchers[1]->getId(), + EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[1]->getId(), metric1ActivationTrigger2->atom_matcher_id()); const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1); EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns); @@ -1702,7 +1702,7 @@ TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { EXPECT_EQ(kNotActive, activation1002->state); EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType); - EXPECT_EQ(metricsManager2->mAllAtomMatchers[2]->getId(), + EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[2]->getId(), metric2ActivationTrigger1->atom_matcher_id()); const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2); EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns); @@ -1710,7 +1710,7 @@ TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { EXPECT_EQ(kNotActive, activation1003->state); EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType); - EXPECT_EQ(metricsManager2->mAllAtomMatchers[3]->getId(), + EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[3]->getId(), metric2ActivationTrigger2->atom_matcher_id()); const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3); EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns); diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp index 293e8ed1c44c..33bdc64333e0 100644 --- a/cmds/statsd/tests/UidMap_test.cpp +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -44,7 +44,7 @@ TEST(UidMapTest, TestIsolatedUID) { sp<StatsPullerManager> pullerManager = new StatsPullerManager(); sp<AlarmMonitor> anomalyAlarmMonitor; sp<AlarmMonitor> subscriberAlarmMonitor; - // Construct the processor with a dummy sendBroadcast function that does nothing. + // Construct the processor with a no-op sendBroadcast function that does nothing. StatsLogProcessor p( m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, [](const ConfigKey& key) { return true; }, diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index d96ff8a1925b..ba919f1e0ad8 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -23,7 +23,7 @@ #include "logd/LogEvent.h" #include "metrics_test_helper.h" -#include "src/matchers/SimpleLogMatchingTracker.h" +#include "src/matchers/SimpleAtomMatchingTracker.h" #include "src/metrics/MetricProducer.h" #include "src/stats_log_util.h" #include "stats_event.h" diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 5524ebc86b36..1000aea14868 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -22,7 +22,7 @@ #include <vector> #include "metrics_test_helper.h" -#include "src/matchers/SimpleLogMatchingTracker.h" +#include "src/matchers/SimpleAtomMatchingTracker.h" #include "src/metrics/MetricProducer.h" #include "src/stats_log_util.h" #include "tests/statsd_test_util.h" diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp index 6b50fe5387d7..f6d30618ee15 100644 --- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp +++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp @@ -49,8 +49,8 @@ sp<StatsPullerManager> pullerManager = new StatsPullerManager(); sp<AlarmMonitor> anomalyAlarmMonitor; sp<AlarmMonitor> periodicAlarmMonitor; set<int> allTagIds; -vector<sp<LogMatchingTracker>> oldAtomMatchers; -unordered_map<int64_t, int> oldLogTrackerMap; +vector<sp<AtomMatchingTracker>> oldAtomMatchingTrackers; +unordered_map<int64_t, int> oldAtomMatchingTrackerMap; vector<sp<ConditionTracker>> oldConditionTrackers; vector<sp<MetricProducer>> oldMetricProducers; std::vector<sp<AnomalyTracker>> oldAnomalyTrackers; @@ -71,8 +71,8 @@ public: void SetUp() override { allTagIds.clear(); - oldAtomMatchers.clear(); - oldLogTrackerMap.clear(); + oldAtomMatchingTrackers.clear(); + oldAtomMatchingTrackerMap.clear(); oldConditionTrackers.clear(); oldMetricProducers.clear(); oldAnomalyTrackers.clear(); @@ -89,13 +89,13 @@ public: }; bool initConfig(const StatsdConfig& config) { - return initStatsdConfig(key, config, uidMap, pullerManager, anomalyAlarmMonitor, - periodicAlarmMonitor, timeBaseNs, timeBaseNs, allTagIds, - oldAtomMatchers, oldLogTrackerMap, oldConditionTrackers, - oldMetricProducers, oldAnomalyTrackers, oldAlarmTrackers, - conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, - activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - alertTrackerMap, metricsWithActivation, noReportMetricIds); + return initStatsdConfig( + key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, + timeBaseNs, timeBaseNs, allTagIds, oldAtomMatchingTrackers, oldAtomMatchingTrackerMap, + oldConditionTrackers, oldMetricProducers, oldAnomalyTrackers, oldAlarmTrackers, + conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, + metricsWithActivation, noReportMetricIds); } } // anonymous namespace @@ -111,10 +111,11 @@ TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) { vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN); vector<bool> cycleTracker(1, false); - unordered_map<int64_t, int> newLogTrackerMap; - newLogTrackerMap[matcherId] = 0; - EXPECT_TRUE(determineMatcherUpdateStatus(config, 0, oldLogTrackerMap, oldAtomMatchers, - newLogTrackerMap, matchersToUpdate, cycleTracker)); + unordered_map<int64_t, int> newAtomMatchingTrackerMap; + newAtomMatchingTrackerMap[matcherId] = 0; + EXPECT_TRUE(determineMatcherUpdateStatus(config, 0, oldAtomMatchingTrackerMap, + oldAtomMatchingTrackers, newAtomMatchingTrackerMap, + matchersToUpdate, cycleTracker)); EXPECT_EQ(matchersToUpdate[0], UPDATE_PRESERVE); } @@ -134,10 +135,11 @@ TEST_F(ConfigUpdateTest, TestSimpleMatcherReplace) { vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN); vector<bool> cycleTracker(1, false); - unordered_map<int64_t, int> newLogTrackerMap; - newLogTrackerMap[matcherId] = 0; - EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 0, oldLogTrackerMap, oldAtomMatchers, - newLogTrackerMap, matchersToUpdate, cycleTracker)); + unordered_map<int64_t, int> newAtomMatchingTrackerMap; + newAtomMatchingTrackerMap[matcherId] = 0; + EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap, + oldAtomMatchingTrackers, newAtomMatchingTrackerMap, + matchersToUpdate, cycleTracker)); EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE); } @@ -163,20 +165,21 @@ TEST_F(ConfigUpdateTest, TestCombinationMatcherPreserve) { EXPECT_TRUE(initConfig(config)); StatsdConfig newConfig; - unordered_map<int64_t, int> newLogTrackerMap; + unordered_map<int64_t, int> newAtomMatchingTrackerMap; // Same matchers, different order, all should be preserved. *newConfig.add_atom_matcher() = matcher2; - newLogTrackerMap[matcher2Id] = 0; + newAtomMatchingTrackerMap[matcher2Id] = 0; *newConfig.add_atom_matcher() = matcher3; - newLogTrackerMap[matcher3Id] = 1; + newAtomMatchingTrackerMap[matcher3Id] = 1; *newConfig.add_atom_matcher() = matcher1; - newLogTrackerMap[matcher1Id] = 2; + newAtomMatchingTrackerMap[matcher1Id] = 2; vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN); vector<bool> cycleTracker(3, false); // Only update the combination. It should recurse the two child matchers and preserve all 3. - EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldLogTrackerMap, oldAtomMatchers, - newLogTrackerMap, matchersToUpdate, cycleTracker)); + EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap, + oldAtomMatchingTrackers, newAtomMatchingTrackerMap, + matchersToUpdate, cycleTracker)); EXPECT_EQ(matchersToUpdate[0], UPDATE_PRESERVE); EXPECT_EQ(matchersToUpdate[1], UPDATE_PRESERVE); EXPECT_EQ(matchersToUpdate[2], UPDATE_PRESERVE); @@ -207,19 +210,20 @@ TEST_F(ConfigUpdateTest, TestCombinationMatcherReplace) { matcher3.mutable_combination()->set_operation(LogicalOperation::AND); StatsdConfig newConfig; - unordered_map<int64_t, int> newLogTrackerMap; + unordered_map<int64_t, int> newAtomMatchingTrackerMap; *newConfig.add_atom_matcher() = matcher2; - newLogTrackerMap[matcher2Id] = 0; + newAtomMatchingTrackerMap[matcher2Id] = 0; *newConfig.add_atom_matcher() = matcher3; - newLogTrackerMap[matcher3Id] = 1; + newAtomMatchingTrackerMap[matcher3Id] = 1; *newConfig.add_atom_matcher() = matcher1; - newLogTrackerMap[matcher1Id] = 2; + newAtomMatchingTrackerMap[matcher1Id] = 2; vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN); vector<bool> cycleTracker(3, false); // Only update the combination. The simple matchers should not be evaluated. - EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldLogTrackerMap, oldAtomMatchers, - newLogTrackerMap, matchersToUpdate, cycleTracker)); + EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap, + oldAtomMatchingTrackers, newAtomMatchingTrackerMap, + matchersToUpdate, cycleTracker)); EXPECT_EQ(matchersToUpdate[0], UPDATE_UNKNOWN); EXPECT_EQ(matchersToUpdate[1], UPDATE_REPLACE); EXPECT_EQ(matchersToUpdate[2], UPDATE_UNKNOWN); @@ -250,19 +254,20 @@ TEST_F(ConfigUpdateTest, TestCombinationMatcherDepsChange) { matcher2.mutable_simple_atom_matcher()->set_atom_id(12); StatsdConfig newConfig; - unordered_map<int64_t, int> newLogTrackerMap; + unordered_map<int64_t, int> newAtomMatchingTrackerMap; *newConfig.add_atom_matcher() = matcher2; - newLogTrackerMap[matcher2Id] = 0; + newAtomMatchingTrackerMap[matcher2Id] = 0; *newConfig.add_atom_matcher() = matcher3; - newLogTrackerMap[matcher3Id] = 1; + newAtomMatchingTrackerMap[matcher3Id] = 1; *newConfig.add_atom_matcher() = matcher1; - newLogTrackerMap[matcher1Id] = 2; + newAtomMatchingTrackerMap[matcher1Id] = 2; vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN); vector<bool> cycleTracker(3, false); // Only update the combination. - EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldLogTrackerMap, oldAtomMatchers, - newLogTrackerMap, matchersToUpdate, cycleTracker)); + EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap, + oldAtomMatchingTrackers, newAtomMatchingTrackerMap, + matchersToUpdate, cycleTracker)); // Matcher 2 and matcher3 must be reevaluated. Matcher 1 might, but does not need to be. EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE); EXPECT_EQ(matchersToUpdate[1], UPDATE_REPLACE); @@ -330,47 +335,48 @@ TEST_F(ConfigUpdateTest, TestUpdateMatchers) { *newConfig.add_atom_matcher() = combination1; set<int> newTagIds; - unordered_map<int64_t, int> newLogTrackerMap; - vector<sp<LogMatchingTracker>> newAtomMatchers; - EXPECT_TRUE(updateLogTrackers(newConfig, uidMap, oldLogTrackerMap, oldAtomMatchers, newTagIds, - newLogTrackerMap, newAtomMatchers)); + unordered_map<int64_t, int> newAtomMatchingTrackerMap; + vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers; + EXPECT_TRUE(updateAtomTrackers(newConfig, uidMap, oldAtomMatchingTrackerMap, + oldAtomMatchingTrackers, newTagIds, newAtomMatchingTrackerMap, + newAtomMatchingTrackers)); ASSERT_EQ(newTagIds.size(), 3); EXPECT_EQ(newTagIds.count(10), 1); EXPECT_EQ(newTagIds.count(111), 1); EXPECT_EQ(newTagIds.count(13), 1); - ASSERT_EQ(newLogTrackerMap.size(), 6); - EXPECT_EQ(newLogTrackerMap.at(combination3Id), 0); - EXPECT_EQ(newLogTrackerMap.at(simple2Id), 1); - EXPECT_EQ(newLogTrackerMap.at(combination2Id), 2); - EXPECT_EQ(newLogTrackerMap.at(simple1Id), 3); - EXPECT_EQ(newLogTrackerMap.at(simple4Id), 4); - EXPECT_EQ(newLogTrackerMap.at(combination1Id), 5); + ASSERT_EQ(newAtomMatchingTrackerMap.size(), 6); + EXPECT_EQ(newAtomMatchingTrackerMap.at(combination3Id), 0); + EXPECT_EQ(newAtomMatchingTrackerMap.at(simple2Id), 1); + EXPECT_EQ(newAtomMatchingTrackerMap.at(combination2Id), 2); + EXPECT_EQ(newAtomMatchingTrackerMap.at(simple1Id), 3); + EXPECT_EQ(newAtomMatchingTrackerMap.at(simple4Id), 4); + EXPECT_EQ(newAtomMatchingTrackerMap.at(combination1Id), 5); - ASSERT_EQ(newAtomMatchers.size(), 6); + ASSERT_EQ(newAtomMatchingTrackers.size(), 6); // Make sure all atom matchers are initialized: - for (const sp<LogMatchingTracker>& tracker : newAtomMatchers) { + for (const sp<AtomMatchingTracker>& tracker : newAtomMatchingTrackers) { EXPECT_TRUE(tracker->mInitialized); } // Make sure preserved atom matchers are the same. - EXPECT_EQ(oldAtomMatchers[oldLogTrackerMap.at(simple1Id)], - newAtomMatchers[newLogTrackerMap.at(simple1Id)]); - EXPECT_EQ(oldAtomMatchers[oldLogTrackerMap.at(combination1Id)], - newAtomMatchers[newLogTrackerMap.at(combination1Id)]); + EXPECT_EQ(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(simple1Id)], + newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(simple1Id)]); + EXPECT_EQ(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(combination1Id)], + newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(combination1Id)]); // Make sure replaced matchers are different. - EXPECT_NE(oldAtomMatchers[oldLogTrackerMap.at(simple2Id)], - newAtomMatchers[newLogTrackerMap.at(simple2Id)]); - EXPECT_NE(oldAtomMatchers[oldLogTrackerMap.at(combination2Id)], - newAtomMatchers[newLogTrackerMap.at(combination2Id)]); + EXPECT_NE(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(simple2Id)], + newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(simple2Id)]); + EXPECT_NE(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(combination2Id)], + newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(combination2Id)]); // Validation, make sure the matchers have the proper ids. Could do more checks here. - EXPECT_EQ(newAtomMatchers[0]->getId(), combination3Id); - EXPECT_EQ(newAtomMatchers[1]->getId(), simple2Id); - EXPECT_EQ(newAtomMatchers[2]->getId(), combination2Id); - EXPECT_EQ(newAtomMatchers[3]->getId(), simple1Id); - EXPECT_EQ(newAtomMatchers[4]->getId(), simple4Id); - EXPECT_EQ(newAtomMatchers[5]->getId(), combination1Id); + EXPECT_EQ(newAtomMatchingTrackers[0]->getId(), combination3Id); + EXPECT_EQ(newAtomMatchingTrackers[1]->getId(), simple2Id); + EXPECT_EQ(newAtomMatchingTrackers[2]->getId(), combination2Id); + EXPECT_EQ(newAtomMatchingTrackers[3]->getId(), simple1Id); + EXPECT_EQ(newAtomMatchingTrackers[4]->getId(), simple4Id); + EXPECT_EQ(newAtomMatchingTrackers[5]->getId(), combination1Id); } } // namespace statsd diff --git a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp index 4e97eaf6f149..d6db4c12ae4d 100644 --- a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp +++ b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp @@ -24,7 +24,7 @@ #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "src/condition/ConditionTracker.h" -#include "src/matchers/LogMatchingTracker.h" +#include "src/matchers/AtomMatchingTracker.h" #include "src/metrics/CountMetricProducer.h" #include "src/metrics/GaugeMetricProducer.h" #include "src/metrics/MetricProducer.h" @@ -383,7 +383,7 @@ TEST(MetricsManagerTest, TestInitialConditions) { sp<AlarmMonitor> periodicAlarmMonitor; StatsdConfig config = buildConfigWithDifferentPredicates(); set<int> allTagIds; - vector<sp<LogMatchingTracker>> allAtomMatchers; + vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; unordered_map<int64_t, int> logTrackerMap; vector<sp<ConditionTracker>> allConditionTrackers; vector<sp<MetricProducer>> allMetricProducers; @@ -400,7 +400,7 @@ TEST(MetricsManagerTest, TestInitialConditions) { EXPECT_TRUE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, logTrackerMap, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap, allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, @@ -432,7 +432,7 @@ TEST(MetricsManagerTest, TestGoodConfig) { sp<AlarmMonitor> periodicAlarmMonitor; StatsdConfig config = buildGoodConfig(); set<int> allTagIds; - vector<sp<LogMatchingTracker>> allAtomMatchers; + vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; unordered_map<int64_t, int> logTrackerMap; vector<sp<ConditionTracker>> allConditionTrackers; vector<sp<MetricProducer>> allMetricProducers; @@ -449,7 +449,7 @@ TEST(MetricsManagerTest, TestGoodConfig) { EXPECT_TRUE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, logTrackerMap, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap, allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, @@ -469,7 +469,7 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { sp<AlarmMonitor> periodicAlarmMonitor; StatsdConfig config = buildDimensionMetricsWithMultiTags(); set<int> allTagIds; - vector<sp<LogMatchingTracker>> allAtomMatchers; + vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; unordered_map<int64_t, int> logTrackerMap; vector<sp<ConditionTracker>> allConditionTrackers; vector<sp<MetricProducer>> allMetricProducers; @@ -486,7 +486,7 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, logTrackerMap, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap, allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, @@ -500,7 +500,7 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { sp<AlarmMonitor> periodicAlarmMonitor; StatsdConfig config = buildCircleMatchers(); set<int> allTagIds; - vector<sp<LogMatchingTracker>> allAtomMatchers; + vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; unordered_map<int64_t, int> logTrackerMap; vector<sp<ConditionTracker>> allConditionTrackers; vector<sp<MetricProducer>> allMetricProducers; @@ -517,7 +517,7 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, logTrackerMap, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap, allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, @@ -531,7 +531,7 @@ TEST(MetricsManagerTest, TestMissingMatchers) { sp<AlarmMonitor> periodicAlarmMonitor; StatsdConfig config = buildMissingMatchers(); set<int> allTagIds; - vector<sp<LogMatchingTracker>> allAtomMatchers; + vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; unordered_map<int64_t, int> logTrackerMap; vector<sp<ConditionTracker>> allConditionTrackers; vector<sp<MetricProducer>> allMetricProducers; @@ -547,7 +547,7 @@ TEST(MetricsManagerTest, TestMissingMatchers) { std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, logTrackerMap, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap, allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, @@ -561,7 +561,7 @@ TEST(MetricsManagerTest, TestMissingPredicate) { sp<AlarmMonitor> periodicAlarmMonitor; StatsdConfig config = buildMissingPredicate(); set<int> allTagIds; - vector<sp<LogMatchingTracker>> allAtomMatchers; + vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; unordered_map<int64_t, int> logTrackerMap; vector<sp<ConditionTracker>> allConditionTrackers; vector<sp<MetricProducer>> allMetricProducers; @@ -577,7 +577,7 @@ TEST(MetricsManagerTest, TestMissingPredicate) { std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, logTrackerMap, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap, allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, @@ -591,7 +591,7 @@ TEST(MetricsManagerTest, TestCirclePredicateDependency) { sp<AlarmMonitor> periodicAlarmMonitor; StatsdConfig config = buildCirclePredicates(); set<int> allTagIds; - vector<sp<LogMatchingTracker>> allAtomMatchers; + vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; unordered_map<int64_t, int> logTrackerMap; vector<sp<ConditionTracker>> allConditionTrackers; vector<sp<MetricProducer>> allMetricProducers; @@ -608,7 +608,7 @@ TEST(MetricsManagerTest, TestCirclePredicateDependency) { EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, logTrackerMap, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap, allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, @@ -622,7 +622,7 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) { sp<AlarmMonitor> periodicAlarmMonitor; StatsdConfig config = buildAlertWithUnknownMetric(); set<int> allTagIds; - vector<sp<LogMatchingTracker>> allAtomMatchers; + vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers; unordered_map<int64_t, int> logTrackerMap; vector<sp<ConditionTracker>> allConditionTrackers; vector<sp<MetricProducer>> allMetricProducers; @@ -639,21 +639,21 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) { EXPECT_FALSE(initStatsdConfig( kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, logTrackerMap, + timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap, allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation, noReportMetricIds)); } -TEST(MetricsManagerTest, TestCreateLogTrackerInvalidMatcher) { +TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerInvalidMatcher) { sp<UidMap> uidMap = new UidMap(); AtomMatcher matcher; matcher.set_id(21); - EXPECT_EQ(createLogTracker(matcher, 0, uidMap), nullptr); + EXPECT_EQ(createAtomMatchingTracker(matcher, 0, uidMap), nullptr); } -TEST(MetricsManagerTest, TestCreateLogTrackerSimple) { +TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerSimple) { int index = 1; int64_t id = 123; sp<UidMap> uidMap = new UidMap(); @@ -666,7 +666,7 @@ TEST(MetricsManagerTest, TestCreateLogTrackerSimple) { simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( android::view::DisplayStateEnum::DISPLAY_STATE_ON); - sp<LogMatchingTracker> tracker = createLogTracker(matcher, index, uidMap); + sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, index, uidMap); EXPECT_NE(tracker, nullptr); EXPECT_TRUE(tracker->mInitialized); @@ -677,7 +677,7 @@ TEST(MetricsManagerTest, TestCreateLogTrackerSimple) { EXPECT_EQ(atomIds.count(util::SCREEN_STATE_CHANGED), 1); } -TEST(MetricsManagerTest, TestCreateLogTrackerCombination) { +TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerCombination) { int index = 1; int64_t id = 123; sp<UidMap> uidMap = new UidMap(); @@ -688,7 +688,7 @@ TEST(MetricsManagerTest, TestCreateLogTrackerCombination) { combination->add_matcher(123); combination->add_matcher(223); - sp<LogMatchingTracker> tracker = createLogTracker(matcher, index, uidMap); + sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, index, uidMap); EXPECT_NE(tracker, nullptr); // Combination matchers need to be initialized first. diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 0be983f2a9b0..1761d5d9e1fa 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -16,7 +16,7 @@ #include <aidl/android/util/StatsEventParcel.h> -#include "matchers/SimpleLogMatchingTracker.h" +#include "matchers/SimpleAtomMatchingTracker.h" #include "stats_event.h" using aidl::android::util::StatsEventParcel; @@ -1008,8 +1008,8 @@ sp<EventMatcherWizard> createEventMatcherWizard( } uint64_t matcherHash = 0x12345678; int64_t matcherId = 678; - return new EventMatcherWizard({new SimpleLogMatchingTracker(matcherId, matcherIndex, - matcherHash, atomMatcher, uidMap)}); + return new EventMatcherWizard({new SimpleAtomMatchingTracker( + matcherId, matcherIndex, matcherHash, atomMatcher, uidMap)}); } void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId, diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt index ab2f42b97e3b..f43bd2bd61b9 100644 --- a/config/boot-image-profile.txt +++ b/config/boot-image-profile.txt @@ -33629,6 +33629,35 @@ HSPLjava/math/BigInteger;-><init>([B)V HSPLjava/math/BigInteger;->abs()Ljava/math/BigInteger; HSPLjava/math/BigInteger;->add(Ljava/math/BigInteger;)Ljava/math/BigInteger; HSPLjava/math/BigInteger;->bitLength()I +#Temporary manual additions to avoid slowing tests down too much +#Carefully positioned for clean merge +HSPLjava/math/BigInteger;->add([IJ)[I +HSPLjava/math/BigInteger;->add([I[I)[I +HSPLjava/math/BigInteger;->subtract([IJ)[I +HSPLjava/math/BigInteger;->subtract([I[I)[I +HSPLjava/math/BigInteger;->jacobiSymbol(ILjava/math/BigInteger;)I +HSPLjava/math/BigInteger;->lucasLehmerSequence(ILjava/math/BigInteger;Ljava/math/BigInteger;)Ljava/math/BigInteger; +HSPLjava/math/BigInteger;->multiplyToLen([II[II[I)[I +HSPLjava/math/MutableBigInteger;->add(Ljava/math/MutableBigInteger;)V +HSPLjava/math/MutableBigInteger;->addShifted(Ljava/math/MutableBigInteger;)V +HSPLjava/math/MutableBigInteger;->subtract(Ljava/math/MutableBigInteger;)V +HSPLjava/math/MutableBigInteger;->difference(Ljava/math/MutableBigInteger;)V +HSPLjava/math/MutableBigInteger;->divideKnuth(Ljava/math/MutableBigInteger;Ljava/math/MutableBigInteger;Z)Ljava/math/MutableBigInteger; +HSPLjava/math/MutableBigInteger;->divideMagnitude(Ljava/math/MutableBigInteger;Ljava/math/MutableBigInteger;Z)Ljava/math/MutableBigInteger; +HSPLjava/math/MutableBigInteger;->divideLongMagnitude(JLjava/math/MutableBigInteger;)Ljava/math/MutableBigInteger; +HSPLjava/math/MutableBigInteger;->divideOneWord(ILjava/math/MutableBigInteger;)I +HSPLjava/math/MutableBigInteger;->divadd([I[II)I +HSPLjava/math/MutableBigInteger;->mulsub([I[IIII)I +HSPLjava/math/MutableBigInteger;->mulsubBorrow([I[IIII)I +HSPLjava/math/MutableBigInteger;->copyAndShift([III[III)V +HSPLjava/math/MutableBigInteger;->multiply(Ljava/math/MutableBigInteger;Ljava/math/MutableBigInteger;)V +HSPLjava/math/MutableBigInteger;->mul(ILjava/math/MutableBigInteger;)V +HSPLjava/math/MutableBigInteger;->multiply(ILjava/math/MutableBigInteger;)V +HSPLjava/math/MutableBigInteger;->primitiveRightShift(I)I +HSPLjava/math/MutableBigInteger;->primitiveLeftShift(I)I +HSPLjava/math/MutableBigInteger;->binaryGCD(Ljava/math/MutableBigInteger;)Ljava/math/MutableBigInteger; +HSPLjava/math/MutableBigInteger;->binaryGCD(II)I +#End of maual additions HSPLjava/math/BigInteger;->compareTo(Ljava/math/BigInteger;)I HSPLjava/math/BigInteger;->divide(Ljava/math/BigInteger;)Ljava/math/BigInteger; HSPLjava/math/BigInteger;->divideAndRemainder(Ljava/math/BigInteger;)[Ljava/math/BigInteger; diff --git a/config/preloaded-classes b/config/preloaded-classes index e43c7d42868c..f56656b69ec4 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -3709,9 +3709,9 @@ android.media.IRemoteVolumeObserver android.media.IRingtonePlayer$Stub$Proxy android.media.IRingtonePlayer$Stub android.media.IRingtonePlayer -android.media.IStrategyPreferredDeviceDispatcher$Stub$Proxy -android.media.IStrategyPreferredDeviceDispatcher$Stub -android.media.IStrategyPreferredDeviceDispatcher +android.media.IStrategyPreferredDevicesDispatcher$Stub$Proxy +android.media.IStrategyPreferredDevicesDispatcher$Stub +android.media.IStrategyPreferredDevicesDispatcher android.media.IVolumeController$Stub$Proxy android.media.IVolumeController$Stub android.media.IVolumeController diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 7cec717f96e0..d67b98620f37 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -424,9 +424,14 @@ public final class ActivityThread extends ClientTransactionHandler { final String authority; final int userId; + @GuardedBy("mLock") + ContentProviderHolder mHolder; // Temp holder to be used between notifier and waiter + Object mLock; // The lock to be used to get notified when the provider is ready + public ProviderKey(String authority, int userId) { this.authority = authority; this.userId = userId; + this.mLock = new Object(); } @Override @@ -440,7 +445,11 @@ public final class ActivityThread extends ClientTransactionHandler { @Override public int hashCode() { - return ((authority != null) ? authority.hashCode() : 0) ^ userId; + return hashCode(authority, userId); + } + + public static int hashCode(final String auth, final int userIdent) { + return ((auth != null) ? auth.hashCode() : 0) ^ userIdent; } } @@ -461,9 +470,8 @@ public final class ActivityThread extends ClientTransactionHandler { // Mitigation for b/74523247: Used to serialize calls to AM.getContentProvider(). // Note we never removes items from this map but that's okay because there are only so many // users and so many authorities. - // TODO Remove it once we move CPR.wait() from AMS to the client side. - @GuardedBy("mGetProviderLocks") - final ArrayMap<ProviderKey, Object> mGetProviderLocks = new ArrayMap<>(); + @GuardedBy("mGetProviderKeys") + final SparseArray<ProviderKey> mGetProviderKeys = new SparseArray<>(); final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners = new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>(); @@ -1756,6 +1764,16 @@ public final class ActivityThread extends ClientTransactionHandler { ActivityThread.this, activityToken, actionId, arguments, cancellationSignal, resultCallback)); } + + @Override + public void notifyContentProviderPublishStatus(@NonNull ContentProviderHolder holder, + @NonNull String auth, int userId, boolean published) { + final ProviderKey key = getGetProviderKey(auth, userId); + synchronized (key.mLock) { + key.mHolder = holder; + key.mLock.notifyAll(); + } + } } private @NonNull SafeCancellationTransport createSafeCancellationTransport( @@ -6807,13 +6825,33 @@ public final class ActivityThread extends ClientTransactionHandler { // provider since it might take a long time to run and it could also potentially // be re-entrant in the case where the provider is in the same process. ContentProviderHolder holder = null; + final ProviderKey key = getGetProviderKey(auth, userId); try { - synchronized (getGetProviderLock(auth, userId)) { + synchronized (key) { holder = ActivityManager.getService().getContentProvider( getApplicationThread(), c.getOpPackageName(), auth, userId, stable); + // If the returned holder is non-null but its provider is null and it's not + // local, we'll need to wait for the publishing of the provider. + if (holder != null && holder.provider == null && !holder.mLocal) { + synchronized (key.mLock) { + key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS); + holder = key.mHolder; + } + if (holder != null && holder.provider == null) { + // probably timed out + holder = null; + } + } } } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); + } catch (InterruptedException e) { + holder = null; + } finally { + // Clear the holder from the key since the key itself is never cleared. + synchronized (key.mLock) { + key.mHolder = null; + } } if (holder == null) { if (UserManager.get(c).isUserUnlocked(userId)) { @@ -6831,13 +6869,13 @@ public final class ActivityThread extends ClientTransactionHandler { return holder.provider; } - private Object getGetProviderLock(String auth, int userId) { - final ProviderKey key = new ProviderKey(auth, userId); - synchronized (mGetProviderLocks) { - Object lock = mGetProviderLocks.get(key); + private ProviderKey getGetProviderKey(String auth, int userId) { + final int key = ProviderKey.hashCode(auth, userId); + synchronized (mGetProviderKeys) { + ProviderKey lock = mGetProviderKeys.get(key); if (lock == null) { - lock = key; - mGetProviderLocks.put(key, lock); + lock = new ProviderKey(auth, userId); + mGetProviderKeys.put(key, lock); } return lock; } diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java index 15237beee805..08cd0b34ee0a 100644 --- a/core/java/android/app/ApplicationLoaders.java +++ b/core/java/android/app/ApplicationLoaders.java @@ -243,8 +243,8 @@ public class ApplicationLoaders { // cached must be built and loaded in the same environment if (!sharedLibrariesEquals(sharedLibraries, cached.sharedLibraries)) { - Log.w(TAG, "Unexpected environment for cached library: (" + sharedLibraries + "|" - + cached.sharedLibraries + ")"); + Log.w(TAG, "Unexpected environment loading cached library " + zip + " (real|cached): (" + + sharedLibraries + "|" + cached.sharedLibraries + ")"); return null; } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index dedd8705ef55..676c6c01d349 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -684,8 +684,7 @@ public class ApplicationPackageManager extends PackageManager { @Override public int checkPermission(String permName, String pkgName) { - return PermissionManager - .checkPackageNamePermission(permName, pkgName, getUserId()); + return PermissionManager.checkPackageNamePermission(permName, pkgName, getUserId()); } @Override diff --git a/core/java/android/app/ContentProviderHolder.java b/core/java/android/app/ContentProviderHolder.java index 3d745831ce1c..e330a30de7b0 100644 --- a/core/java/android/app/ContentProviderHolder.java +++ b/core/java/android/app/ContentProviderHolder.java @@ -39,6 +39,11 @@ public class ContentProviderHolder implements Parcelable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public boolean noReleaseNeeded; + /** + * Whether the provider here is a local provider or not. + */ + public boolean mLocal; + @UnsupportedAppUsage public ContentProviderHolder(ProviderInfo _info) { info = _info; @@ -59,6 +64,7 @@ public class ContentProviderHolder implements Parcelable { } dest.writeStrongBinder(connection); dest.writeInt(noReleaseNeeded ? 1 : 0); + dest.writeInt(mLocal ? 1 : 0); } public static final @android.annotation.NonNull Parcelable.Creator<ContentProviderHolder> CREATOR @@ -81,5 +87,6 @@ public class ContentProviderHolder implements Parcelable { source.readStrongBinder()); connection = source.readStrongBinder(); noReleaseNeeded = source.readInt() != 0; + mLocal = source.readInt() != 0; } -}
\ No newline at end of file +} diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 93dfc79109a6..f37ca61ba69f 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -461,7 +461,7 @@ interface IActivityManager { @UnsupportedAppUsage Rect getTaskBounds(int taskId); @UnsupportedAppUsage - boolean setProcessMemoryTrimLevel(in String process, int uid, int level); + boolean setProcessMemoryTrimLevel(in String process, int userId, int level); // Start of L transactions diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 24da50481df3..dc9918ade9c2 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -16,6 +16,7 @@ package android.app; +import android.app.ContentProviderHolder; import android.app.IInstrumentationWatcher; import android.app.IUiAutomationConnection; import android.app.ProfilerInfo; @@ -147,4 +148,6 @@ oneway interface IApplicationThread { void performDirectAction(IBinder activityToken, String actionId, in Bundle arguments, in RemoteCallback cancellationCallback, in RemoteCallback resultCallback); + void notifyContentProviderPublishStatus(in ContentProviderHolder holder, String auth, + int userId, boolean published); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 68e65612971c..6737972dc34e 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1492,6 +1492,7 @@ public class Notification implements Parcelable private boolean mAllowGeneratedReplies = true; private final @SemanticAction int mSemanticAction; private final boolean mIsContextual; + private boolean mAuthenticationRequired; /** * Small icon representing the action. @@ -1528,6 +1529,7 @@ public class Notification implements Parcelable mAllowGeneratedReplies = in.readInt() == 1; mSemanticAction = in.readInt(); mIsContextual = in.readInt() == 1; + mAuthenticationRequired = in.readInt() == 1; } /** @@ -1536,13 +1538,14 @@ public class Notification implements Parcelable @Deprecated public Action(int icon, CharSequence title, PendingIntent intent) { this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true, - SEMANTIC_ACTION_NONE, false /* isContextual */); + SEMANTIC_ACTION_NONE, false /* isContextual */, false /* requireAuth */); } /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */ private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras, RemoteInput[] remoteInputs, boolean allowGeneratedReplies, - @SemanticAction int semanticAction, boolean isContextual) { + @SemanticAction int semanticAction, boolean isContextual, + boolean requireAuth) { this.mIcon = icon; if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) { this.icon = icon.getResId(); @@ -1554,6 +1557,7 @@ public class Notification implements Parcelable this.mAllowGeneratedReplies = allowGeneratedReplies; this.mSemanticAction = semanticAction; this.mIsContextual = isContextual; + this.mAuthenticationRequired = requireAuth; } /** @@ -1624,6 +1628,17 @@ public class Notification implements Parcelable } /** + * Returns whether the OS should only send this action's {@link PendingIntent} on an + * unlocked device. + * + * If the device is locked when the action is invoked, the OS should show the keyguard and + * require successful authentication before invoking the intent. + */ + public boolean isAuthenticationRequired() { + return mAuthenticationRequired; + } + + /** * Builder class for {@link Action} objects. */ public static final class Builder { @@ -1635,6 +1650,7 @@ public class Notification implements Parcelable @Nullable private ArrayList<RemoteInput> mRemoteInputs; private @SemanticAction int mSemanticAction; private boolean mIsContextual; + private boolean mAuthenticationRequired; /** * Construct a new builder for {@link Action} object. @@ -1654,7 +1670,7 @@ public class Notification implements Parcelable * @param intent the {@link PendingIntent} to fire when users trigger this action */ public Builder(Icon icon, CharSequence title, PendingIntent intent) { - this(icon, title, intent, new Bundle(), null, true, SEMANTIC_ACTION_NONE); + this(icon, title, intent, new Bundle(), null, true, SEMANTIC_ACTION_NONE, false); } /** @@ -1665,23 +1681,25 @@ public class Notification implements Parcelable public Builder(Action action) { this(action.getIcon(), action.title, action.actionIntent, new Bundle(action.mExtras), action.getRemoteInputs(), - action.getAllowGeneratedReplies(), action.getSemanticAction()); + action.getAllowGeneratedReplies(), action.getSemanticAction(), + action.isAuthenticationRequired()); } private Builder(@Nullable Icon icon, @Nullable CharSequence title, @Nullable PendingIntent intent, @NonNull Bundle extras, @Nullable RemoteInput[] remoteInputs, boolean allowGeneratedReplies, - @SemanticAction int semanticAction) { + @SemanticAction int semanticAction, boolean authRequired) { mIcon = icon; mTitle = title; mIntent = intent; mExtras = extras; if (remoteInputs != null) { - mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length); + mRemoteInputs = new ArrayList<>(remoteInputs.length); Collections.addAll(mRemoteInputs, remoteInputs); } mAllowGeneratedReplies = allowGeneratedReplies; mSemanticAction = semanticAction; + mAuthenticationRequired = authRequired; } /** @@ -1776,6 +1794,21 @@ public class Notification implements Parcelable } /** + * Sets whether the OS should only send this action's {@link PendingIntent} on an + * unlocked device. + * + * If this is true and the device is locked when the action is invoked, the OS will + * show the keyguard and require successful authentication before invoking the intent. + * If this is false and the device is locked, the OS will decide whether authentication + * should be required. + */ + @NonNull + public Builder setAuthenticationRequired(boolean authenticationRequired) { + mAuthenticationRequired = authenticationRequired; + return this; + } + + /** * Throws an NPE if we are building a contextual action missing one of the fields * necessary to display the action. */ @@ -1827,7 +1860,8 @@ public class Notification implements Parcelable RemoteInput[] textInputsArr = textInputs.isEmpty() ? null : textInputs.toArray(new RemoteInput[textInputs.size()]); return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr, - mAllowGeneratedReplies, mSemanticAction, mIsContextual); + mAllowGeneratedReplies, mSemanticAction, mIsContextual, + mAuthenticationRequired); } } @@ -1841,7 +1875,8 @@ public class Notification implements Parcelable getRemoteInputs(), getAllowGeneratedReplies(), getSemanticAction(), - isContextual()); + isContextual(), + isAuthenticationRequired()); } @Override @@ -1870,6 +1905,7 @@ public class Notification implements Parcelable out.writeInt(mAllowGeneratedReplies ? 1 : 0); out.writeInt(mSemanticAction); out.writeInt(mIsContextual ? 1 : 0); + out.writeInt(mAuthenticationRequired ? 1 : 0); } public static final @android.annotation.NonNull Parcelable.Creator<Action> CREATOR = @@ -5151,19 +5187,11 @@ public class Notification implements Parcelable bindHeaderChronometerAndTime(contentView, p); bindProfileBadge(contentView, p); bindAlertedIcon(contentView, p); - bindActivePermissions(contentView, p); bindFeedbackIcon(contentView, p); bindExpandButton(contentView, p); mN.mUsesStandardHeader = true; } - private void bindActivePermissions(RemoteViews contentView, StandardTemplateParams p) { - int color = getNeutralColor(p); - contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP); - contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP); - contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP); - } - private void bindFeedbackIcon(RemoteViews contentView, StandardTemplateParams p) { int color = getNeutralColor(p); contentView.setDrawableTint(R.id.feedback, false, color, PorterDuff.Mode.SRC_ATOP); diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 8ee995d6e6be..0627bc855934 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -282,6 +282,16 @@ public class NotificationManager { = "android.app.action.INTERRUPTION_FILTER_CHANGED"; /** + * Intent that is broadcast when the state of + * {@link #hasEnabledNotificationListener(String, UserHandle)} changes. + * @hide + */ + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final String ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED = + "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED"; + + /** * Intent that is broadcast when the state of getCurrentInterruptionFilter() changes. * @hide */ diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index 54f3f1026050..6c6c04e4e975 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -413,7 +413,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { public final void disableLocal() { synchronized (mLock) { mDisabled = true; - mCache.clear(); + clear(); } } @@ -463,7 +463,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { cacheName(), mCache.size(), mLastSeenNonce, currentNonce)); } - mCache.clear(); + clear(); mLastSeenNonce = currentNonce; cachedResult = null; } @@ -728,9 +728,13 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * It's better to use explicit cork and uncork pairs that tighly surround big batches of * invalidations, but it's not always practical to tell where these invalidation batches * might occur. AutoCorker's time-based corking is a decent alternative. + * + * The auto-cork delay is configurable but it should not be too long. The purpose of + * the delay is to minimize the number of times a server writes to the system property + * when invalidating the cache. One write every 50ms does not hurt system performance. */ public static final class AutoCorker { - public static final int DEFAULT_AUTO_CORK_DELAY_MS = 2000; + public static final int DEFAULT_AUTO_CORK_DELAY_MS = 50; private final String mPropertyName; private final int mAutoCorkDelayMs; diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING index fe509dea2def..4139b2faeb1b 100644 --- a/core/java/android/app/TEST_MAPPING +++ b/core/java/android/app/TEST_MAPPING @@ -65,6 +65,33 @@ { "name": "CtsInstantAppTests", "file_patterns": ["(/|^)InstantAppResolve[^/]*"] + }, + { + "name": "CtsAutoFillServiceTestCases", + "options": [ + { + "include-filter": "android.autofillservice.cts.PreSimpleSaveActivityTest" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ], + "file_patterns": ["(/|^)Activity.java"] + }, + { + "name": "CtsAutoFillServiceTestCases", + "options": [ + { + "include-filter": "android.autofillservice.cts.SimpleSaveActivityTest" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "android.platform.test.annotations.AppModeFull" + } + ], + "file_patterns": ["(/|^)Activity.java"] } ], "postsubmit": [ diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java index bd1eea51f8af..cef6ab094177 100644 --- a/core/java/android/app/slice/SliceProvider.java +++ b/core/java/android/app/slice/SliceProvider.java @@ -155,10 +155,6 @@ public abstract class SliceProvider extends ContentProvider { /** * @hide */ - public static final String EXTRA_PROVIDER_PKG = "provider_pkg"; - /** - * @hide - */ public static final String EXTRA_RESULT = "result"; private static final boolean DEBUG = false; @@ -519,7 +515,6 @@ public abstract class SliceProvider extends ContentProvider { com.android.internal.R.string.config_slicePermissionComponent))); intent.putExtra(EXTRA_BIND_URI, sliceUri); intent.putExtra(EXTRA_PKG, callingPackage); - intent.putExtra(EXTRA_PROVIDER_PKG, context.getPackageName()); // Unique pending intent. intent.setData(sliceUri.buildUpon().appendQueryParameter("package", callingPackage) .build()); diff --git a/core/java/android/app/timezonedetector/TimeZoneCapabilities.java b/core/java/android/app/timezonedetector/TimeZoneCapabilities.java index 236b0064763e..cc0af3f97e49 100644 --- a/core/java/android/app/timezonedetector/TimeZoneCapabilities.java +++ b/core/java/android/app/timezonedetector/TimeZoneCapabilities.java @@ -91,11 +91,13 @@ public final class TimeZoneCapabilities implements Parcelable { private final @UserIdInt int mUserId; private final @CapabilityState int mConfigureAutoDetectionEnabled; + private final @CapabilityState int mConfigureGeoDetectionEnabled; private final @CapabilityState int mSuggestManualTimeZone; private TimeZoneCapabilities(@NonNull Builder builder) { this.mUserId = builder.mUserId; this.mConfigureAutoDetectionEnabled = builder.mConfigureAutoDetectionEnabled; + this.mConfigureGeoDetectionEnabled = builder.mConfigureGeoDetectionEnabled; this.mSuggestManualTimeZone = builder.mSuggestManualTimeZone; } @@ -103,6 +105,7 @@ public final class TimeZoneCapabilities implements Parcelable { private static TimeZoneCapabilities createFromParcel(Parcel in) { return new TimeZoneCapabilities.Builder(in.readInt()) .setConfigureAutoDetectionEnabled(in.readInt()) + .setConfigureGeoDetectionEnabled(in.readInt()) .setSuggestManualTimeZone(in.readInt()) .build(); } @@ -111,6 +114,7 @@ public final class TimeZoneCapabilities implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mUserId); dest.writeInt(mConfigureAutoDetectionEnabled); + dest.writeInt(mConfigureGeoDetectionEnabled); dest.writeInt(mSuggestManualTimeZone); } @@ -120,8 +124,8 @@ public final class TimeZoneCapabilities implements Parcelable { } /** - * Returns the user's capability state for controlling automatic time zone detection via - * {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and {@link + * Returns the user's capability state for controlling whether automatic time zone detection is + * enabled via {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and {@link * TimeZoneConfiguration#isAutoDetectionEnabled()}. */ @CapabilityState @@ -130,6 +134,16 @@ public final class TimeZoneCapabilities implements Parcelable { } /** + * Returns the user's capability state for controlling whether geolocation can be used to detect + * time zone via {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and {@link + * TimeZoneConfiguration#isGeoDetectionEnabled()}. + */ + @CapabilityState + public int getConfigureGeoDetectionEnabled() { + return mConfigureGeoDetectionEnabled; + } + + /** * Returns the user's capability state for manually setting the time zone on a device via * {@link TimeZoneDetector#suggestManualTimeZone(ManualTimeZoneSuggestion)}. * @@ -157,12 +171,16 @@ public final class TimeZoneCapabilities implements Parcelable { TimeZoneCapabilities that = (TimeZoneCapabilities) o; return mUserId == that.mUserId && mConfigureAutoDetectionEnabled == that.mConfigureAutoDetectionEnabled + && mConfigureGeoDetectionEnabled == that.mConfigureGeoDetectionEnabled && mSuggestManualTimeZone == that.mSuggestManualTimeZone; } @Override public int hashCode() { - return Objects.hash(mUserId, mConfigureAutoDetectionEnabled, mSuggestManualTimeZone); + return Objects.hash(mUserId, + mConfigureAutoDetectionEnabled, + mConfigureGeoDetectionEnabled, + mSuggestManualTimeZone); } @Override @@ -170,6 +188,7 @@ public final class TimeZoneCapabilities implements Parcelable { return "TimeZoneDetectorCapabilities{" + "mUserId=" + mUserId + ", mConfigureAutomaticDetectionEnabled=" + mConfigureAutoDetectionEnabled + + ", mConfigureGeoDetectionEnabled=" + mConfigureGeoDetectionEnabled + ", mSuggestManualTimeZone=" + mSuggestManualTimeZone + '}'; } @@ -179,6 +198,7 @@ public final class TimeZoneCapabilities implements Parcelable { private final @UserIdInt int mUserId; private @CapabilityState int mConfigureAutoDetectionEnabled; + private @CapabilityState int mConfigureGeoDetectionEnabled; private @CapabilityState int mSuggestManualTimeZone; /** @@ -194,6 +214,12 @@ public final class TimeZoneCapabilities implements Parcelable { return this; } + /** Sets the state for the geolocation time zone detection enabled config. */ + public Builder setConfigureGeoDetectionEnabled(@CapabilityState int value) { + this.mConfigureGeoDetectionEnabled = value; + return this; + } + /** Sets the state for the suggestManualTimeZone action. */ public Builder setSuggestManualTimeZone(@CapabilityState int value) { this.mSuggestManualTimeZone = value; @@ -204,6 +230,7 @@ public final class TimeZoneCapabilities implements Parcelable { @NonNull public TimeZoneCapabilities build() { verifyCapabilitySet(mConfigureAutoDetectionEnabled, "configureAutoDetectionEnabled"); + verifyCapabilitySet(mConfigureGeoDetectionEnabled, "configureGeoDetectionEnabled"); verifyCapabilitySet(mSuggestManualTimeZone, "suggestManualTimeZone"); return new TimeZoneCapabilities(this); } diff --git a/core/java/android/app/timezonedetector/TimeZoneConfiguration.java b/core/java/android/app/timezonedetector/TimeZoneConfiguration.java index 047d3493d5dd..6f84ee22a985 100644 --- a/core/java/android/app/timezonedetector/TimeZoneConfiguration.java +++ b/core/java/android/app/timezonedetector/TimeZoneConfiguration.java @@ -67,6 +67,10 @@ public final class TimeZoneConfiguration implements Parcelable { @Property public static final String PROPERTY_AUTO_DETECTION_ENABLED = "autoDetectionEnabled"; + /** See {@link TimeZoneConfiguration#isGeoDetectionEnabled()} for details. */ + @Property + public static final String PROPERTY_GEO_DETECTION_ENABLED = "geoDetectionEnabled"; + private final Bundle mBundle; private TimeZoneConfiguration(Builder builder) { @@ -86,7 +90,8 @@ public final class TimeZoneConfiguration implements Parcelable { /** Returns {@code true} if all known properties are set. */ public boolean isComplete() { - return hasProperty(PROPERTY_AUTO_DETECTION_ENABLED); + return hasProperty(PROPERTY_AUTO_DETECTION_ENABLED) + && hasProperty(PROPERTY_GEO_DETECTION_ENABLED); } /** Returns true if the specified property is set. */ @@ -108,6 +113,28 @@ public final class TimeZoneConfiguration implements Parcelable { return mBundle.getBoolean(PROPERTY_AUTO_DETECTION_ENABLED); } + /** + * Returns the value of the {@link #PROPERTY_GEO_DETECTION_ENABLED} property. This + * controls whether a device can use location to determine time zone. Only used when + * {@link #isAutoDetectionEnabled()} is true. + * + * @throws IllegalStateException if the field has not been set + */ + public boolean isGeoDetectionEnabled() { + if (!mBundle.containsKey(PROPERTY_GEO_DETECTION_ENABLED)) { + throw new IllegalStateException(PROPERTY_GEO_DETECTION_ENABLED + " is not set"); + } + return mBundle.getBoolean(PROPERTY_GEO_DETECTION_ENABLED); + } + + /** + * Convenience method to merge this with another. The argument configuration properties have + * precedence. + */ + public TimeZoneConfiguration with(TimeZoneConfiguration other) { + return new Builder(this).mergeProperties(other).build(); + } + @Override public int describeContents() { return 0; @@ -174,6 +201,12 @@ public final class TimeZoneConfiguration implements Parcelable { return this; } + /** Sets the desired state of the geolocation time zone detection enabled property. */ + public Builder setGeoDetectionEnabled(boolean enabled) { + this.mBundle.putBoolean(PROPERTY_GEO_DETECTION_ENABLED, enabled); + return this; + } + /** Returns the {@link TimeZoneConfiguration}. */ @NonNull public TimeZoneConfiguration build() { diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 3522b1b8aff5..0e6a0637d801 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -199,7 +199,7 @@ public final class UsageEvents implements Parcelable { public static final int NOTIFICATION_INTERRUPTION = 12; /** - * A Slice was pinned by the default launcher or the default assistant. + * A Slice was pinned by the default assistant. * @hide */ @SystemApi diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index ee9bd3d259fb..7fe29a920931 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -277,6 +277,14 @@ public class IntentFilter implements Parcelable { public static final String SCHEME_HTTPS = "https"; /** + * Package scheme + * + * @see #addDataScheme(String) + * @hide + */ + public static final String SCHEME_PACKAGE = "package"; + + /** * The value to indicate a wildcard for incoming match arguments. * @hide */ diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 043953d1aabd..0c6810c07394 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -1134,6 +1134,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * @hide */ @SystemApi + @TestApi public int targetSandboxVersion; /** diff --git a/core/java/android/content/pm/InstantAppRequest.java b/core/java/android/content/pm/InstantAppRequest.java index 84f5021f0538..5b5d109fb863 100644 --- a/core/java/android/content/pm/InstantAppRequest.java +++ b/core/java/android/content/pm/InstantAppRequest.java @@ -18,6 +18,7 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.content.Intent; import android.os.Bundle; @@ -40,7 +41,7 @@ public final class InstantAppRequest { /** Whether or not the requesting package was an instant app */ public final boolean isRequesterInstantApp; /** ID of the user requesting the instant application */ - public final int userId; + public final @UserIdInt int userId; /** * Optional extra bundle provided by the source application to the installer for additional * verification. @@ -60,7 +61,7 @@ public final class InstantAppRequest { public InstantAppRequest(AuxiliaryResolveInfo responseObj, Intent origIntent, String resolvedType, String callingPackage, @Nullable String callingFeatureId, - boolean isRequesterInstantApp, int userId, Bundle verificationBundle, + boolean isRequesterInstantApp, @UserIdInt int userId, Bundle verificationBundle, boolean resolveForStart, @Nullable int[] hostDigestPrefixSecure, @NonNull String token) { this.responseObj = responseObj; diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java index 67bda2ce3944..0a913acba9f5 100644 --- a/core/java/android/content/pm/IntentFilterVerificationInfo.java +++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java @@ -56,19 +56,19 @@ public final class IntentFilterVerificationInfo implements Parcelable { private ArraySet<String> mDomains = new ArraySet<>(); private String mPackageName; - private int mMainStatus; + private int mStatus; /** @hide */ public IntentFilterVerificationInfo() { mPackageName = null; - mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; + mStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; } /** @hide */ public IntentFilterVerificationInfo(String packageName, ArraySet<String> domains) { mPackageName = packageName; mDomains = domains; - mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; + mStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; } /** @hide */ @@ -87,14 +87,14 @@ public final class IntentFilterVerificationInfo implements Parcelable { } public int getStatus() { - return mMainStatus; + return mStatus; } /** @hide */ public void setStatus(int s) { if (s >= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED && s <= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { - mMainStatus = s; + mStatus = s; } else { Log.w(TAG, "Trying to set a non supported status: " + s); } @@ -156,7 +156,7 @@ public final class IntentFilterVerificationInfo implements Parcelable { if (status == -1) { Log.e(TAG, "Unknown status value: " + status); } - mMainStatus = status; + mStatus = status; int outerDepth = parser.getDepth(); int type; @@ -184,7 +184,7 @@ public final class IntentFilterVerificationInfo implements Parcelable { /** @hide */ public void writeToXml(XmlSerializer serializer) throws IOException { serializer.attribute(null, ATTR_PACKAGE_NAME, mPackageName); - serializer.attribute(null, ATTR_STATUS, String.valueOf(mMainStatus)); + serializer.attribute(null, ATTR_STATUS, String.valueOf(mStatus)); for (String str : mDomains) { serializer.startTag(null, TAG_DOMAIN); serializer.attribute(null, ATTR_DOMAIN_NAME, str); @@ -194,7 +194,7 @@ public final class IntentFilterVerificationInfo implements Parcelable { /** @hide */ public String getStatusString() { - return getStatusStringFromValue(((long)mMainStatus) << 32); + return getStatusStringFromValue(((long) mStatus) << 32); } /** @hide */ @@ -233,7 +233,7 @@ public final class IntentFilterVerificationInfo implements Parcelable { private void readFromParcel(Parcel source) { mPackageName = source.readString(); - mMainStatus = source.readInt(); + mStatus = source.readInt(); ArrayList<String> list = new ArrayList<>(); source.readStringList(list); mDomains.addAll(list); @@ -242,7 +242,7 @@ public final class IntentFilterVerificationInfo implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mPackageName); - dest.writeInt(mMainStatus); + dest.writeInt(mStatus); dest.writeStringList(new ArrayList<>(mDomains)); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index e08af5534afd..7b2955db3318 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3373,6 +3373,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @TestApi public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 1 << 5; /** diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java index bc418061e1d1..b0437ac7284e 100644 --- a/core/java/android/content/res/ApkAssets.java +++ b/core/java/android/content/res/ApkAssets.java @@ -101,14 +101,11 @@ public final class ApkAssets { public @interface FormatType {} @GuardedBy("this") - private final long mNativePtr; + private long mNativePtr; // final, except cleared in finalizer. @Nullable @GuardedBy("this") - private final StringBlock mStringBlock; - - @GuardedBy("this") - private boolean mOpen = true; + private final StringBlock mStringBlock; // null or closed if mNativePtr = 0. @PropertyFlags private final int mFlags; @@ -380,12 +377,16 @@ public final class ApkAssets { /** @hide */ @Nullable public OverlayableInfo getOverlayableInfo(String overlayableName) throws IOException { - return nativeGetOverlayableInfo(mNativePtr, overlayableName); + synchronized (this) { + return nativeGetOverlayableInfo(mNativePtr, overlayableName); + } } /** @hide */ public boolean definesOverlayable() throws IOException { - return nativeDefinesOverlayable(mNativePtr); + synchronized (this) { + return nativeDefinesOverlayable(mNativePtr); + } } /** @@ -412,12 +413,12 @@ public final class ApkAssets { */ public void close() { synchronized (this) { - if (mOpen) { - mOpen = false; + if (mNativePtr != 0) { if (mStringBlock != null) { mStringBlock.close(); } nativeDestroy(mNativePtr); + mNativePtr = 0; } } } diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index a605f5d68f06..f86eb9082e01 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -300,26 +300,6 @@ public class BiometricManager { } /** - * Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password) - * - * @param userId this operation takes effect for. - * @param hardwareAuthToken an opaque token returned by password confirmation. - * @hide - */ - @RequiresPermission(USE_BIOMETRIC_INTERNAL) - public void resetLockout(int userId, byte[] hardwareAuthToken) { - if (mService != null) { - try { - mService.resetLockout(userId, hardwareAuthToken); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } else { - Slog.w(TAG, "resetLockout(): Service not connected"); - } - } - - /** * Get a list of AuthenticatorIDs for biometric authenticators which have 1) enrolled templates, * and 2) meet the requirements for integrating with Keystore. The AuthenticatorIDs are known * in Keystore land as SIDs, and are used during key generation. diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl index c1d0ad44850d..dd6aa736a1a3 100644 --- a/core/java/android/hardware/biometrics/IAuthService.aidl +++ b/core/java/android/hardware/biometrics/IAuthService.aidl @@ -46,9 +46,6 @@ interface IAuthService { // Register callback for when keyguard biometric eligibility changes. void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback); - // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password) - void resetLockout(int userId, in byte [] hardwareAuthToken); - // Get a list of AuthenticatorIDs for authenticators which have enrolled templates and meet // the requirements for integrating with Keystore. The AuthenticatorID are known in Keystore // land as SIDs, and are used during key generation. diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl index 8eb22dabbf3c..5e6fe6885f9d 100644 --- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl +++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl @@ -53,9 +53,6 @@ interface IBiometricAuthenticator { // Return the LockoutTracker status for the specified user int getLockoutModeForUser(int userId); - // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password) - void resetLockout(int userId, in byte [] hardwareAuthToken); - // Gets the authenticator ID representing the current set of enrolled templates long getAuthenticatorId(int callingUserId); } diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl index a5b3abb30cbb..005ed324da77 100644 --- a/core/java/android/hardware/biometrics/IBiometricService.aidl +++ b/core/java/android/hardware/biometrics/IBiometricService.aidl @@ -56,9 +56,6 @@ interface IBiometricService { // Client lifecycle is still managed in <Biometric>Service. void onReadyForAuthentication(int cookie); - // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password) - void resetLockout(int userId, in byte [] hardwareAuthToken); - // Get a list of AuthenticatorIDs for authenticators which have enrolled templates and meet // the requirements for integrating with Keystore. The AuthenticatorID are known in Keystore // land as SIDs, and are used during key generation. diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 19cb13c7e114..1b114d3528a2 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -72,6 +72,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan private static final int MSG_SET_FEATURE_COMPLETED = 107; private static final int MSG_CHALLENGE_GENERATED = 108; private static final int MSG_FACE_DETECTED = 109; + private static final int MSG_CHALLENGE_INTERRUPTED = 110; + private static final int MSG_CHALLENGE_INTERRUPT_FINISHED = 111; private final IFaceService mService; private final Context mContext; @@ -141,14 +143,19 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } @Override - public void onChallengeGenerated(long challenge) { - if (mGenerateChallengeCallback instanceof InternalGenerateChallengeCallback) { - // Perform this on system_server thread, since the application's thread is - // blocked waiting for the result - mGenerateChallengeCallback.onGenerateChallengeResult(challenge); - } else { - mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, challenge).sendToTarget(); - } + public void onChallengeGenerated(int sensorId, long challenge) { + mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, 0, challenge) + .sendToTarget(); + } + + @Override + public void onChallengeInterrupted(int sensorId) { + mHandler.obtainMessage(MSG_CHALLENGE_INTERRUPTED, sensorId).sendToTarget(); + } + + @Override + public void onChallengeInterruptFinished(int sensorId) { + mHandler.obtainMessage(MSG_CHALLENGE_INTERRUPT_FINISHED, sensorId).sendToTarget(); } }; @@ -405,35 +412,6 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } /** - * Same as {@link #generateChallenge(GenerateChallengeCallback)}, except blocks until the - * TEE/hardware operation is complete. - * @return challenge generated in the TEE/hardware - * @hide - */ - @RequiresPermission(MANAGE_BIOMETRIC) - public long generateChallengeBlocking() { - final AtomicReference<Long> result = new AtomicReference<>(); - final CountDownLatch latch = new CountDownLatch(1); - final GenerateChallengeCallback callback = new InternalGenerateChallengeCallback() { - @Override - public void onGenerateChallengeResult(long challenge) { - result.set(challenge); - latch.countDown(); - } - }; - - generateChallenge(callback); - - try { - latch.await(1, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Slog.e(TAG, "Interrupted while generatingChallenge", e); - e.printStackTrace(); - } - return result.get(); - } - - /** * Generates a unique random challenge in the TEE. A typical use case is to have it wrapped in a * HardwareAuthenticationToken, minted by Gatekeeper upon PIN/Pattern/Password verification. * The HardwareAuthenticationToken can then be sent to the biometric HAL together with a @@ -446,11 +424,12 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * @hide */ @RequiresPermission(MANAGE_BIOMETRIC) - public void generateChallenge(GenerateChallengeCallback callback) { + public void generateChallenge(int sensorId, GenerateChallengeCallback callback) { if (mService != null) { try { mGenerateChallengeCallback = callback; - mService.generateChallenge(mToken, mServiceReceiver, mContext.getOpPackageName()); + mService.generateChallenge(mToken, sensorId, mServiceReceiver, + mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -458,15 +437,66 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } /** - * Invalidates the current auth token. + * Same as {@link #generateChallenge(int, GenerateChallengeCallback)}, but assumes the first + * enumerated sensor. + * @hide + */ + @RequiresPermission(MANAGE_BIOMETRIC) + public void generateChallenge(GenerateChallengeCallback callback) { + final List<FaceSensorProperties> faceSensorProperties = getSensorProperties(); + if (faceSensorProperties.isEmpty()) { + Slog.e(TAG, "No sensors"); + return; + } + + final int sensorId = faceSensorProperties.get(0).sensorId; + generateChallenge(sensorId, callback); + } + + /** + * Invalidates the current challenge. * * @hide */ @RequiresPermission(MANAGE_BIOMETRIC) public void revokeChallenge() { + final List<FaceSensorProperties> faceSensorProperties = getSensorProperties(); + if (faceSensorProperties.isEmpty()) { + Slog.e(TAG, "No sensors during revokeChallenge"); + } + revokeChallenge(faceSensorProperties.get(0).sensorId); + } + + /** + * Invalidates the current challenge. + * + * @hide + */ + @RequiresPermission(MANAGE_BIOMETRIC) + public void revokeChallenge(int sensorId) { if (mService != null) { try { - mService.revokeChallenge(mToken, mContext.getOpPackageName()); + mService.revokeChallenge(mToken, sensorId, mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password) + * + * @param sensorId Sensor ID that this operation takes effect for + * @param userId User ID that this operation takes effect for. + * @param hardwareAuthToken An opaque token returned by password confirmation. + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void resetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) { + if (mService != null) { + try { + mService.resetLockout(mToken, sensorId, userId, hardwareAuthToken, + mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1071,14 +1101,26 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } /** + * Callback structure provided to {@link #generateChallenge(int, GenerateChallengeCallback)}. * @hide */ public interface GenerateChallengeCallback { - void onGenerateChallengeResult(long challenge); - } + /** + * Invoked when a challenge has been generated. + */ + void onGenerateChallengeResult(int sensorId, long challenge); - private abstract static class InternalGenerateChallengeCallback - implements GenerateChallengeCallback {} + /** + * Invoked if the challenge has not been revoked and a subsequent caller/owner invokes + * {@link #generateChallenge(int, GenerateChallengeCallback)}, but + */ + default void onChallengeInterrupted(int sensorId) {} + + /** + * Invoked when the interrupting client has finished (e.g. revoked its challenge). + */ + default void onChallengeInterruptFinished(int sensorId) {} + } private class OnEnrollCancelListener implements OnCancelListener { @Override @@ -1151,12 +1193,18 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan args.recycle(); break; case MSG_CHALLENGE_GENERATED: - sendChallengeGenerated((long) msg.obj /* challenge */); + sendChallengeGenerated(msg.arg1 /* sensorId */, (long) msg.obj /* challenge */); break; case MSG_FACE_DETECTED: sendFaceDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */, (boolean) msg.obj /* isStrongBiometric */); break; + case MSG_CHALLENGE_INTERRUPTED: + sendChallengeInterrupted((int) msg.obj /* sensorId */); + break; + case MSG_CHALLENGE_INTERRUPT_FINISHED: + sendChallengeInterruptFinished((int) msg.obj /* sensorId */); + break; default: Slog.w(TAG, "Unknown message: " + msg.what); } @@ -1178,11 +1226,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan mGetFeatureCallback.onCompleted(success, feature, value); } - private void sendChallengeGenerated(long challenge) { + private void sendChallengeGenerated(int sensorId, long challenge) { if (mGenerateChallengeCallback == null) { return; } - mGenerateChallengeCallback.onGenerateChallengeResult(challenge); + mGenerateChallengeCallback.onGenerateChallengeResult(sensorId, challenge); } private void sendFaceDetected(int sensorId, int userId, boolean isStrongBiometric) { @@ -1193,6 +1241,22 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan mFaceDetectionCallback.onFaceDetected(sensorId, userId, isStrongBiometric); } + private void sendChallengeInterrupted(int sensorId) { + if (mGenerateChallengeCallback == null) { + Slog.e(TAG, "sendChallengeInterrupted, callback null"); + return; + } + mGenerateChallengeCallback.onChallengeInterrupted(sensorId); + } + + private void sendChallengeInterruptFinished(int sensorId) { + if (mGenerateChallengeCallback == null) { + Slog.e(TAG, "sendChallengeInterruptFinished, callback null"); + return; + } + mGenerateChallengeCallback.onChallengeInterruptFinished(sensorId); + } + private void sendRemovedResult(Face face, int remaining) { if (mRemovalCallback == null) { return; diff --git a/core/java/android/hardware/face/FaceSensorProperties.java b/core/java/android/hardware/face/FaceSensorProperties.java index e3b2fbb6c614..1015724a485d 100644 --- a/core/java/android/hardware/face/FaceSensorProperties.java +++ b/core/java/android/hardware/face/FaceSensorProperties.java @@ -25,20 +25,36 @@ import android.os.Parcelable; */ public class FaceSensorProperties implements Parcelable { + /** + * A statically configured ID representing this sensor. Sensor IDs must be unique across all + * biometrics across the device, starting at 0, and in increments of 1. + */ public final int sensorId; + /** + * True if the sensor is able to perform generic face detection, without running the + * matching algorithm, and without affecting the lockout counter. + */ public final boolean supportsFaceDetection; + /** + * True if the sensor is able to provide self illumination in dark scenarios, without support + * from above the HAL. + */ + public final boolean supportsSelfIllumination; /** * Initializes SensorProperties with specified values */ - public FaceSensorProperties(int sensorId, boolean supportsFaceDetection) { + public FaceSensorProperties(int sensorId, boolean supportsFaceDetection, + boolean supportsSelfIllumination) { this.sensorId = sensorId; this.supportsFaceDetection = supportsFaceDetection; + this.supportsSelfIllumination = supportsSelfIllumination; } protected FaceSensorProperties(Parcel in) { sensorId = in.readInt(); supportsFaceDetection = in.readBoolean(); + supportsSelfIllumination = in.readBoolean(); } public static final Creator<FaceSensorProperties> CREATOR = @@ -63,5 +79,6 @@ public class FaceSensorProperties implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(sensorId); dest.writeBoolean(supportsFaceDetection); + dest.writeBoolean(supportsSelfIllumination); } } diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index a9097d401349..437feb13b845 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -83,10 +83,10 @@ interface IFaceService { boolean isHardwareDetected(String opPackageName); // Get a pre-enrollment authentication token - void generateChallenge(IBinder token, IFaceServiceReceiver receiver, String opPackageName); + void generateChallenge(IBinder token, int sensorId, IFaceServiceReceiver receiver, String opPackageName); // Finish an enrollment sequence and invalidate the authentication token - void revokeChallenge(IBinder token, String opPackageName); + void revokeChallenge(IBinder token, int sensorId, String opPackageName); // Determine if a user has at least one enrolled face boolean hasEnrolledFaces(int userId, String opPackageName); @@ -98,7 +98,7 @@ interface IFaceService { long getAuthenticatorId(int callingUserId); // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password) - void resetLockout(int userId, in byte [] hardwareAuthToken); + void resetLockout(IBinder token, int sensorId, int userId, in byte [] hardwareAuthToken, String opPackageName); // Add a callback which gets notified when the face lockout period expired. void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName); diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl index 2600b7def03a..bd4d3a0b7017 100644 --- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl +++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl @@ -31,5 +31,7 @@ oneway interface IFaceServiceReceiver { void onRemoved(in Face face, int remaining); void onFeatureSet(boolean success, int feature); void onFeatureGet(boolean success, int feature, boolean value); - void onChallengeGenerated(long challenge); + void onChallengeGenerated(int sensorId, long challenge); + void onChallengeInterrupted(int sensorId); + void onChallengeInterruptFinished(int sensorId); } diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 71598eb6394f..c12bb39c3175 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -18,6 +18,7 @@ package android.hardware.fingerprint; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.MANAGE_FINGERPRINT; +import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT; import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.Manifest.permission.USE_FINGERPRINT; @@ -378,12 +379,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @hide */ public interface GenerateChallengeCallback { - void onChallengeGenerated(long challenge); + void onChallengeGenerated(int sensorId, long challenge); } - private abstract static class InternalGenerateChallengeCallback - implements GenerateChallengeCallback {} - /** * Request authentication of a crypto object. This call warms up the fingerprint hardware * and starts scanning for a fingerprint. It terminates when @@ -593,16 +591,34 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @hide */ @RequiresPermission(MANAGE_FINGERPRINT) - public void generateChallenge(GenerateChallengeCallback callback) { + public void generateChallenge(int sensorId, GenerateChallengeCallback callback) { if (mService != null) try { mGenerateChallengeCallback = callback; - mService.generateChallenge(mToken, mServiceReceiver, mContext.getOpPackageName()); + mService.generateChallenge(mToken, sensorId, mServiceReceiver, + mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** + * Same as {@link #generateChallenge(int, GenerateChallengeCallback)}, but assumes the first + * enumerated sensor. + * @hide + */ + @RequiresPermission(MANAGE_FINGERPRINT) + public void generateChallenge(GenerateChallengeCallback callback) { + final List<FingerprintSensorProperties> fingerprintSensorProperties = getSensorProperties(); + if (fingerprintSensorProperties.isEmpty()) { + Slog.e(TAG, "No sensors"); + return; + } + + final int sensorId = fingerprintSensorProperties.get(0).sensorId; + generateChallenge(sensorId, callback); + } + + /** * Finishes enrollment and cancels the current auth token. * @hide */ @@ -616,6 +632,26 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } /** + * Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password) + * + * @param sensorId Sensor ID that this operation takes effect for + * @param userId User ID that this operation takes effect for. + * @param hardwareAuthToken An opaque token returned by password confirmation. + * @hide + */ + @RequiresPermission(RESET_FINGERPRINT_LOCKOUT) + public void resetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) { + if (mService != null) { + try { + mService.resetLockout(mToken, sensorId, userId, hardwareAuthToken, + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** * Remove given fingerprint template from fingerprint hardware and/or protected storage. * @param fp the fingerprint item to remove * @param userId the user who this fingerprint belongs to @@ -901,7 +937,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing sendRemovedResult((Fingerprint) msg.obj, msg.arg1 /* remaining */); break; case MSG_CHALLENGE_GENERATED: - sendChallengeGenerated((long) msg.obj /* challenge */); + sendChallengeGenerated(msg.arg1 /* sensorId */, (long) msg.obj /* challenge */); break; case MSG_FINGERPRINT_DETECTED: sendFingerprintDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */, @@ -989,12 +1025,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } - private void sendChallengeGenerated(long challenge) { + private void sendChallengeGenerated(int sensorId, long challenge) { if (mGenerateChallengeCallback == null) { Slog.e(TAG, "sendChallengeGenerated, callback null"); return; } - mGenerateChallengeCallback.onChallengeGenerated(challenge); + mGenerateChallengeCallback.onChallengeGenerated(sensorId, challenge); } private void sendFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric) { @@ -1178,14 +1214,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } @Override // binder call - public void onChallengeGenerated(long challenge) { - if (mGenerateChallengeCallback instanceof InternalGenerateChallengeCallback) { - // Perform this on system_server thread, since the application's thread is - // blocked waiting for the result - mGenerateChallengeCallback.onChallengeGenerated(challenge); - } else { - mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, challenge).sendToTarget(); - } + public void onChallengeGenerated(int sensorId, long challenge) { + mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, 0, challenge) + .sendToTarget(); } }; diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java index a774121c43f4..b28551c52b9e 100644 --- a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java +++ b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java @@ -45,18 +45,24 @@ public class FingerprintSensorProperties implements Parcelable { public final int sensorId; public final @SensorType int sensorType; + // IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT + // cannot be checked + public final boolean resetLockoutRequiresHardwareAuthToken; /** * Initializes SensorProperties with specified values */ - public FingerprintSensorProperties(int sensorId, @SensorType int sensorType) { + public FingerprintSensorProperties(int sensorId, @SensorType int sensorType, + boolean resetLockoutRequiresHardwareAuthToken) { this.sensorId = sensorId; this.sensorType = sensorType; + this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken; } protected FingerprintSensorProperties(Parcel in) { sensorId = in.readInt(); sensorType = in.readInt(); + resetLockoutRequiresHardwareAuthToken = in.readBoolean(); } public static final Creator<FingerprintSensorProperties> CREATOR = @@ -81,5 +87,6 @@ public class FingerprintSensorProperties implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(sensorId); dest.writeInt(sensorType); + dest.writeBoolean(resetLockoutRequiresHardwareAuthToken); } } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index f6069d81934f..0fae15648e15 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -88,7 +88,7 @@ interface IFingerprintService { boolean isHardwareDetected(String opPackageName); // Get a pre-enrollment authentication token - void generateChallenge(IBinder token, IFingerprintServiceReceiver receiver, String opPackageName); + void generateChallenge(IBinder token, int sensorId, IFingerprintServiceReceiver receiver, String opPackageName); // Finish an enrollment sequence and invalidate the authentication token void revokeChallenge(IBinder token, String opPackageName); @@ -103,7 +103,7 @@ interface IFingerprintService { long getAuthenticatorId(int callingUserId); // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password) - void resetLockout(int userId, in byte [] hardwareAuthToken); + void resetLockout(IBinder token, int sensorId, int userId, in byte[] hardwareAuthToken, String opPackageNAame); // Add a callback which gets notified when the fingerprint lockout period expired. void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName); diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl index ad8fbc087ed0..095b8e9527ad 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl @@ -29,5 +29,5 @@ oneway interface IFingerprintServiceReceiver { void onAuthenticationFailed(); void onError(int error, int vendorCode); void onRemoved(in Fingerprint fp, int remaining); - void onChallengeGenerated(long challenge); + void onChallengeGenerated(int sensorId, long challenge); } diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl index 32530da74e89..a57726c4afe4 100644 --- a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl +++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl @@ -25,4 +25,7 @@ oneway interface IUdfpsOverlayController { // Hides the overlay. void hideUdfpsOverlay(); + + // Shows debug messages on the UDFPS overlay. + void setDebugMessage(String message); } diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index 7234eb1d81cd..cd26079a7bac 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -503,6 +503,10 @@ public class NetworkTemplate implements Parcelable { for (final int ratType : ratTypes) { collapsedRatTypes.add(NetworkTemplate.getCollapsedRatType(ratType)); } + // Add NETWORK_TYPE_5G_NSA to the returned list since 5G NSA is a virtual RAT type and + // it is not in TelephonyManager#NETWORK_TYPE_* constants. + // See {@link NetworkTemplate#NETWORK_TYPE_5G_NSA}. + collapsedRatTypes.add(NetworkTemplate.getCollapsedRatType(NETWORK_TYPE_5G_NSA)); // Ensure that unknown type is returned. collapsedRatTypes.add(TelephonyManager.NETWORK_TYPE_UNKNOWN); return toIntArray(collapsedRatTypes); diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 1050c7f8b4f4..8e865e7ab313 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -1012,10 +1012,16 @@ public class Build { /** * Q. - * <p> - * <em>Why? Why, to give you a taste of your future, a preview of things - * to come. Con permiso, Capitan. The hall is rented, the orchestra - * engaged. It's now time to see if you can dance.</em> + * + * <p>Applications targeting this or a later release will get these new changes in behavior. + * For more information about this release, see the + * <a href="/about/versions/10">Android 10 overview</a>.</p> + * <ul> + * <li><a href="/about/versions/10/behavior-changes-all">Behavior changes: all apps</a></li> + * <li><a href="/about/versions/10/behavior-changes-10">Behavior changes: apps targeting API + * 29+</a></li> + * </ul> + * */ public static final int Q = 29; diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index a6b869d19867..1eb3fc11df7b 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -64,10 +64,11 @@ public class GraphicsEnvironment { private static final String SYSTEM_DRIVER_NAME = "system"; private static final String SYSTEM_DRIVER_VERSION_NAME = ""; private static final long SYSTEM_DRIVER_VERSION_CODE = 0; - private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0"; + private static final String PROPERTY_GFX_DRIVER_PRODUCTION = "ro.gfx.driver.0"; private static final String PROPERTY_GFX_DRIVER_PRERELEASE = "ro.gfx.driver.1"; private static final String PROPERTY_GFX_DRIVER_BUILD_TIME = "ro.gfx.driver_build_time"; - private static final String METADATA_DRIVER_BUILD_TIME = "com.android.gamedriver.build_time"; + private static final String METADATA_DRIVER_BUILD_TIME = + "com.android.graphics.updatabledriver.build_time"; private static final String METADATA_DEVELOPER_DRIVER_ENABLE = "com.android.graphics.developerdriver.enable"; private static final String METADATA_INJECT_LAYERS_ENABLE = @@ -78,20 +79,20 @@ public class GraphicsEnvironment { private static final String ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE = "android.app.action.ANGLE_FOR_ANDROID_TOAST_MESSAGE"; private static final String INTENT_KEY_A4A_TOAST_MESSAGE = "A4A Toast Message"; - private static final String GAME_DRIVER_ALLOWLIST_ALL = "*"; - private static final String GAME_DRIVER_SPHAL_LIBRARIES_FILENAME = "sphal_libraries.txt"; + private static final String UPDATABLE_DRIVER_ALLOWLIST_ALL = "*"; + private static final String UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME = "sphal_libraries.txt"; private static final int VULKAN_1_0 = 0x00400000; private static final int VULKAN_1_1 = 0x00401000; - // GAME_DRIVER_ALL_APPS + // UPDATABLE_DRIVER_ALL_APPS // 0: Default (Invalid values fallback to default as well) - // 1: All apps use Game Driver - // 2: All apps use Prerelease Driver + // 1: All apps use updatable production driver + // 2: All apps use updatable prerelease driver // 3: All apps use system graphics driver - private static final int GAME_DRIVER_GLOBAL_OPT_IN_DEFAULT = 0; - private static final int GAME_DRIVER_GLOBAL_OPT_IN_GAME_DRIVER = 1; - private static final int GAME_DRIVER_GLOBAL_OPT_IN_PRERELEASE_DRIVER = 2; - private static final int GAME_DRIVER_GLOBAL_OPT_IN_OFF = 3; + private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_DEFAULT = 0; + private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRODUCTION_DRIVER = 1; + private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRERELEASE_DRIVER = 2; + private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_OFF = 3; private ClassLoader mClassLoader; private String mLibrarySearchPaths; @@ -722,14 +723,17 @@ public class GraphicsEnvironment { * Return the driver package name to use. Return null for system driver. */ private static String chooseDriverInternal(Bundle coreSettings, ApplicationInfo ai) { - final String gameDriver = SystemProperties.get(PROPERTY_GFX_DRIVER); - final boolean hasGameDriver = gameDriver != null && !gameDriver.isEmpty(); + final String productionDriver = SystemProperties.get(PROPERTY_GFX_DRIVER_PRODUCTION); + final boolean hasProductionDriver = productionDriver != null && !productionDriver.isEmpty(); final String prereleaseDriver = SystemProperties.get(PROPERTY_GFX_DRIVER_PRERELEASE); final boolean hasPrereleaseDriver = prereleaseDriver != null && !prereleaseDriver.isEmpty(); - if (!hasGameDriver && !hasPrereleaseDriver) { - if (DEBUG) Log.v(TAG, "Neither Game Driver nor prerelease driver is supported."); + if (!hasProductionDriver && !hasPrereleaseDriver) { + if (DEBUG) { + Log.v(TAG, + "Neither updatable production driver nor prerelease driver is supported."); + } return null; } @@ -745,56 +749,59 @@ public class GraphicsEnvironment { (ai.metaData != null && ai.metaData.getBoolean(METADATA_DEVELOPER_DRIVER_ENABLE)) || isDebuggable(); - // Priority for Game Driver settings global on confliction (Higher priority comes first): - // 1. GAME_DRIVER_ALL_APPS - // 2. GAME_DRIVER_OPT_OUT_APPS - // 3. GAME_DRIVER_PRERELEASE_OPT_IN_APPS - // 4. GAME_DRIVER_OPT_IN_APPS - // 5. GAME_DRIVER_DENYLIST - // 6. GAME_DRIVER_ALLOWLIST - switch (coreSettings.getInt(Settings.Global.GAME_DRIVER_ALL_APPS, 0)) { - case GAME_DRIVER_GLOBAL_OPT_IN_OFF: - if (DEBUG) Log.v(TAG, "Game Driver is turned off on this device."); + // Priority of updatable driver settings on confliction (Higher priority comes first): + // 1. UPDATABLE_DRIVER_ALL_APPS + // 2. UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS + // 3. UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS + // 4. UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS + // 5. UPDATABLE_DRIVER_PRODUCTION_DENYLIST + // 6. UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST + switch (coreSettings.getInt(Settings.Global.UPDATABLE_DRIVER_ALL_APPS, 0)) { + case UPDATABLE_DRIVER_GLOBAL_OPT_IN_OFF: + if (DEBUG) Log.v(TAG, "updatable driver is turned off on this device."); return null; - case GAME_DRIVER_GLOBAL_OPT_IN_GAME_DRIVER: - if (DEBUG) Log.v(TAG, "All apps opt in to use Game Driver."); - return hasGameDriver ? gameDriver : null; - case GAME_DRIVER_GLOBAL_OPT_IN_PRERELEASE_DRIVER: - if (DEBUG) Log.v(TAG, "All apps opt in to use prerelease driver."); + case UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRODUCTION_DRIVER: + if (DEBUG) Log.v(TAG, "All apps opt in to use updatable production driver."); + return hasProductionDriver ? productionDriver : null; + case UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRERELEASE_DRIVER: + if (DEBUG) Log.v(TAG, "All apps opt in to use updatable prerelease driver."); return hasPrereleaseDriver && enablePrereleaseDriver ? prereleaseDriver : null; - case GAME_DRIVER_GLOBAL_OPT_IN_DEFAULT: + case UPDATABLE_DRIVER_GLOBAL_OPT_IN_DEFAULT: default: break; } final String appPackageName = ai.packageName; - if (getGlobalSettingsString(null, coreSettings, Settings.Global.GAME_DRIVER_OPT_OUT_APPS) + if (getGlobalSettingsString(null, coreSettings, + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS) .contains(appPackageName)) { - if (DEBUG) Log.v(TAG, "App opts out for Game Driver."); + if (DEBUG) Log.v(TAG, "App opts out for updatable production driver."); return null; } if (getGlobalSettingsString( - null, coreSettings, Settings.Global.GAME_DRIVER_PRERELEASE_OPT_IN_APPS) + null, coreSettings, Settings.Global.UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS) .contains(appPackageName)) { - if (DEBUG) Log.v(TAG, "App opts in for prerelease Game Driver."); + if (DEBUG) Log.v(TAG, "App opts in for updatable prerelease driver."); return hasPrereleaseDriver && enablePrereleaseDriver ? prereleaseDriver : null; } - // Early return here since the rest logic is only for Game Driver. - if (!hasGameDriver) { - if (DEBUG) Log.v(TAG, "Game Driver is not supported on the device."); + // Early return here since the rest logic is only for updatable production Driver. + if (!hasProductionDriver) { + if (DEBUG) Log.v(TAG, "Updatable production driver is not supported on the device."); return null; } final boolean isOptIn = - getGlobalSettingsString(null, coreSettings, Settings.Global.GAME_DRIVER_OPT_IN_APPS) + getGlobalSettingsString(null, coreSettings, + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS) .contains(appPackageName); final List<String> allowlist = - getGlobalSettingsString(null, coreSettings, Settings.Global.GAME_DRIVER_ALLOWLIST); - if (!isOptIn && allowlist.indexOf(GAME_DRIVER_ALLOWLIST_ALL) != 0 + getGlobalSettingsString(null, coreSettings, + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST); + if (!isOptIn && allowlist.indexOf(UPDATABLE_DRIVER_ALLOWLIST_ALL) != 0 && !allowlist.contains(appPackageName)) { - if (DEBUG) Log.v(TAG, "App is not on the allowlist for Game Driver."); + if (DEBUG) Log.v(TAG, "App is not on the allowlist for updatable production driver."); return null; } @@ -802,13 +809,13 @@ public class GraphicsEnvironment { // terminate early if it's on the denylist and fallback to system driver. if (!isOptIn && getGlobalSettingsString( - null, coreSettings, Settings.Global.GAME_DRIVER_DENYLIST) + null, coreSettings, Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST) .contains(appPackageName)) { - if (DEBUG) Log.v(TAG, "App is on the denylist for Game Driver."); + if (DEBUG) Log.v(TAG, "App is on the denylist for updatable production driver."); return null; } - return gameDriver; + return productionDriver; } /** @@ -873,7 +880,7 @@ public class GraphicsEnvironment { final String driverBuildTime = driverAppInfo.metaData.getString(METADATA_DRIVER_BUILD_TIME); if (driverBuildTime == null || driverBuildTime.isEmpty()) { - throw new IllegalArgumentException("com.android.gamedriver.build_time is not set"); + Log.v(TAG, "com.android.graphics.updatabledriver.build_time is not set"); } // driver_build_time in the meta-data is in "L<Unix epoch timestamp>" format. e.g. L123456. // Long.parseLong will throw if the meta-data "driver_build_time" is not set properly. @@ -901,7 +908,7 @@ public class GraphicsEnvironment { final Context driverContext = context.createPackageContext(driverPackageName, Context.CONTEXT_RESTRICTED); final BufferedReader reader = new BufferedReader(new InputStreamReader( - driverContext.getAssets().open(GAME_DRIVER_SPHAL_LIBRARIES_FILENAME))); + driverContext.getAssets().open(UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME))); final ArrayList<String> assetStrings = new ArrayList<>(); for (String assetString; (assetString = reader.readLine()) != null;) { assetStrings.add(assetString); @@ -913,7 +920,7 @@ public class GraphicsEnvironment { } } catch (IOException e) { if (DEBUG) { - Log.w(TAG, "Failed to load '" + GAME_DRIVER_SPHAL_LIBRARIES_FILENAME + "'"); + Log.w(TAG, "Failed to load '" + UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME + "'"); } } return ""; diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java index 8ad35e7eb37d..e2e61406ba95 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -35,6 +35,8 @@ import android.annotation.NonNull; import android.annotation.SystemApi; import android.app.Service; import android.app.admin.DevicePolicyManager.PermissionGrantState; +import android.compat.annotation.ChangeId; +import android.compat.annotation.Disabled; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -83,6 +85,15 @@ public abstract class PermissionControllerService extends Service { public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService"; /** + * A ChangeId indicating that this device supports camera and mic indicators. Will be "false" + * if present, because the CompatChanges#isChangeEnabled method returns true if the change id + * is not present. + */ + @ChangeId + @Disabled + private static final long CAMERA_MIC_INDICATORS_NOT_PRESENT = 162547999L; + + /** * Revoke a set of runtime permissions for various apps. * * @param requests The permissions to revoke as {@code Map<packageName, List<permission>>} diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 0c190719af57..b9d27e923615 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -25,6 +25,7 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; +import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.IActivityManager; @@ -637,24 +638,25 @@ public final class PermissionManager { private static final class PackageNamePermissionQuery { final String permName; final String pkgName; - final int uid; + final int userId; - PackageNamePermissionQuery(@Nullable String permName, @Nullable String pkgName, int uid) { + PackageNamePermissionQuery(@Nullable String permName, @Nullable String pkgName, + @UserIdInt int userId) { this.permName = permName; this.pkgName = pkgName; - this.uid = uid; + this.userId = userId; } @Override public String toString() { return String.format( - "PackageNamePermissionQuery(pkgName=\"%s\", permName=\"%s, uid=%s\")", - pkgName, permName, uid); + "PackageNamePermissionQuery(pkgName=\"%s\", permName=\"%s, userId=%s\")", + pkgName, permName, userId); } @Override public int hashCode() { - return Objects.hash(permName, pkgName, uid); + return Objects.hash(permName, pkgName, userId); } @Override @@ -670,16 +672,16 @@ public final class PermissionManager { } return Objects.equals(permName, other.permName) && Objects.equals(pkgName, other.pkgName) - && uid == other.uid; + && userId == other.userId; } } /* @hide */ private static int checkPackageNamePermissionUncached( - String permName, String pkgName, int uid) { + String permName, String pkgName, @UserIdInt int userId) { try { return ActivityThread.getPermissionManager().checkPermission( - permName, pkgName, uid); + permName, pkgName, userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -693,7 +695,7 @@ public final class PermissionManager { @Override protected Integer recompute(PackageNamePermissionQuery query) { return checkPackageNamePermissionUncached( - query.permName, query.pkgName, query.uid); + query.permName, query.pkgName, query.userId); } }; @@ -702,9 +704,10 @@ public final class PermissionManager { * * @hide */ - public static int checkPackageNamePermission(String permName, String pkgName, int uid) { + public static int checkPackageNamePermission(String permName, String pkgName, + @UserIdInt int userId) { return sPackageNamePermissionCache.query( - new PackageNamePermissionQuery(permName, pkgName, uid)); + new PackageNamePermissionQuery(permName, pkgName, userId)); } /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 660455e59a4a..6903a995bf7b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8316,6 +8316,13 @@ public final class Settings { public static final String PANIC_GESTURE_ENABLED = "panic_gesture_enabled"; /** + * Whether the panic button (emergency sos) sound should be enabled. + * + * @hide + */ + public static final String PANIC_SOUND_ENABLED = "panic_sound_enabled"; + + /** * Whether the camera launch gesture to double tap the power button when the screen is off * should be disabled. * @@ -8974,6 +8981,14 @@ public final class Settings { public static final String MEDIA_CONTROLS_RESUME = "qs_media_resumption"; /** + * Controls which packages are blocked from persisting in media controls when resumption is + * enabled. The list of packages is set by the user in the Settings app. + * @see Settings.Secure#MEDIA_CONTROLS_RESUME + * @hide + */ + public static final String MEDIA_CONTROLS_RESUME_BLOCKED = "qs_media_resumption_blocked"; + + /** * Controls if window magnification is enabled. * @hide */ @@ -9022,6 +9037,13 @@ public final class Settings { "accessibility_magnification_capability"; /** + * Whether the Adaptive connectivity option is enabled. + * + * @hide + */ + public static final String ADAPTIVE_CONNECTIVITY_ENABLED = "adaptive_connectivity_enabled"; + + /** * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. * @@ -12347,63 +12369,71 @@ public final class Settings { "show_angle_in_use_dialog_box"; /** - * Game Driver global preference for all Apps. + * Updatable driver global preference for all Apps. * 0 = Default - * 1 = All Apps use Game Driver - * 2 = All Apps use system graphics driver + * 1 = All Apps use updatable production driver + * 2 = All apps use updatable prerelease driver + * 3 = All Apps use system graphics driver * @hide */ - public static final String GAME_DRIVER_ALL_APPS = "game_driver_all_apps"; + public static final String UPDATABLE_DRIVER_ALL_APPS = "updatable_driver_all_apps"; /** - * List of Apps selected to use Game Driver. + * List of Apps selected to use updatable production driver. * i.e. <pkg1>,<pkg2>,...,<pkgN> * @hide */ - public static final String GAME_DRIVER_OPT_IN_APPS = "game_driver_opt_in_apps"; + public static final String UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS = + "updatable_driver_production_opt_in_apps"; /** - * List of Apps selected to use prerelease Game Driver. + * List of Apps selected to use updatable prerelease driver. * i.e. <pkg1>,<pkg2>,...,<pkgN> * @hide */ - public static final String GAME_DRIVER_PRERELEASE_OPT_IN_APPS = - "game_driver_prerelease_opt_in_apps"; + public static final String UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS = + "updatable_driver_prerelease_opt_in_apps"; /** - * List of Apps selected not to use Game Driver. + * List of Apps selected not to use updatable production driver. * i.e. <pkg1>,<pkg2>,...,<pkgN> * @hide */ - public static final String GAME_DRIVER_OPT_OUT_APPS = "game_driver_opt_out_apps"; + public static final String UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS = + "updatable_driver_production_opt_out_apps"; /** - * Apps on the denylist that are forbidden to use Game Driver. + * Apps on the denylist that are forbidden to use updatable production driver. * @hide */ - public static final String GAME_DRIVER_DENYLIST = "game_driver_denylist"; + public static final String UPDATABLE_DRIVER_PRODUCTION_DENYLIST = + "updatable_driver_production_denylist"; /** - * List of denylists, each denylist is a denylist for a specific version of Game Driver. + * List of denylists, each denylist is a denylist for a specific version of + * updatable production driver. * @hide */ - public static final String GAME_DRIVER_DENYLISTS = "game_driver_denylists"; + public static final String UPDATABLE_DRIVER_PRODUCTION_DENYLISTS = + "updatable_driver_production_denylists"; /** - * Apps on the allowlist that are allowed to use Game Driver. + * Apps on the allowlist that are allowed to use updatable production driver. * The string is a list of application package names, seperated by comma. * i.e. <apk1>,<apk2>,...,<apkN> * @hide */ - public static final String GAME_DRIVER_ALLOWLIST = "game_driver_allowlist"; + public static final String UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST = + "updatable_driver_production_allowlist"; /** - * List of libraries in sphal accessible by Game Driver + * List of libraries in sphal accessible by updatable driver * The string is a list of library names, separated by colon. * i.e. <lib1>:<lib2>:...:<libN> * @hide */ - public static final String GAME_DRIVER_SPHAL_LIBRARIES = "game_driver_sphal_libraries"; + public static final String UPDATABLE_DRIVER_SPHAL_LIBRARIES = + "updatable_driver_sphal_libraries"; /** * Ordered GPU debug layer list for Vulkan diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java index 914169485979..6eb2a15eec44 100644 --- a/core/java/android/service/autofill/InlinePresentation.java +++ b/core/java/android/service/autofill/InlinePresentation.java @@ -40,6 +40,11 @@ public final class InlinePresentation implements Parcelable { /** * Represents the UI content and the action for the inline suggestion. + * + * <p>The Slice should be constructed using the Content builder provided in the androidx + * autofill library e.g. {@code androidx.autofill.inline.v1.InlineSuggestionUi.Content.Builder} + * and then converted to a Slice with + * {@code androidx.autofill.inline.UiVersions.Content#getSlice()}.</p> */ private final @NonNull Slice mSlice; @@ -90,6 +95,11 @@ public final class InlinePresentation implements Parcelable { * * @param slice * Represents the UI content and the action for the inline suggestion. + * + * <p>The Slice should be constructed using the Content builder provided in the androidx + * autofill library e.g. {@code androidx.autofill.inline.v1.InlineSuggestionUi.Content.Builder} + * and then converted to a Slice with + * {@code androidx.autofill.inline.UiVersions.Content#getSlice()}.</p> * @param inlinePresentationSpec * Specifies the UI specification for the inline suggestion. * @param pinned @@ -118,6 +128,11 @@ public final class InlinePresentation implements Parcelable { /** * Represents the UI content and the action for the inline suggestion. + * + * <p>The Slice should be constructed using the Content builder provided in the androidx + * autofill library e.g. {@code androidx.autofill.inline.v1.InlineSuggestionUi.Content.Builder} + * and then converted to a Slice with + * {@code androidx.autofill.inline.UiVersions.Content#getSlice()}.</p> */ @DataClass.Generated.Member public @NonNull Slice getSlice() { @@ -244,7 +259,7 @@ public final class InlinePresentation implements Parcelable { }; @DataClass.Generated( - time = 1593131904745L, + time = 1596484869201L, codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java", inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size(min=0L) java.lang.String[] getAutofillHints()\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)") diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java index c1998c6009cf..82f60366a814 100644 --- a/core/java/android/view/ImeInsetsSourceConsumer.java +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -69,7 +69,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { } public void onServedEditorChanged(EditorInfo info) { - if (isDummyOrEmptyEditor(info)) { + if (isFallbackOrEmptyEditor(info)) { mShowOnNextImeRender = false; } mFocusedEditor = info; @@ -167,15 +167,15 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { } } - private boolean isDummyOrEmptyEditor(EditorInfo info) { - // TODO(b/123044812): Handle dummy input gracefully in IME Insets API + private boolean isFallbackOrEmptyEditor(EditorInfo info) { + // TODO(b/123044812): Handle fallback input gracefully in IME Insets API return info == null || (info.fieldId <= 0 && info.inputType <= 0); } private boolean isServedEditorRendered() { if (mFocusedEditor == null || mPreRenderedEditor == null - || isDummyOrEmptyEditor(mFocusedEditor) - || isDummyOrEmptyEditor(mPreRenderedEditor)) { + || isFallbackOrEmptyEditor(mFocusedEditor) + || isFallbackOrEmptyEditor(mPreRenderedEditor)) { // No view is focused or ready. return false; } diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java index bf94670e1ca3..6136a80978b7 100644 --- a/core/java/android/view/NotificationHeaderView.java +++ b/core/java/android/view/NotificationHeaderView.java @@ -52,14 +52,12 @@ public class NotificationHeaderView extends ViewGroup { private View mHeaderText; private View mSecondaryHeaderText; private OnClickListener mExpandClickListener; - private OnClickListener mAppOpsListener; private OnClickListener mFeedbackListener; private HeaderTouchListener mTouchListener = new HeaderTouchListener(); private LinearLayout mTransferChip; private NotificationExpandButton mExpandButton; private CachingIconView mIcon; private View mProfileBadge; - private View mAppOps; private View mFeedbackIcon; private boolean mExpanded; private boolean mShowExpandButtonAtEnd; @@ -117,7 +115,6 @@ public class NotificationHeaderView extends ViewGroup { mExpandButton = findViewById(com.android.internal.R.id.expand_button); mIcon = findViewById(com.android.internal.R.id.icon); mProfileBadge = findViewById(com.android.internal.R.id.profile_badge); - mAppOps = findViewById(com.android.internal.R.id.app_ops); mFeedbackIcon = findViewById(com.android.internal.R.id.feedback); } @@ -146,7 +143,6 @@ public class NotificationHeaderView extends ViewGroup { // Icons that should go at the end if ((child == mExpandButton && mShowExpandButtonAtEnd) || child == mProfileBadge - || child == mAppOps || child == mFeedbackIcon || child == mTransferChip) { iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth(); @@ -212,7 +208,6 @@ public class NotificationHeaderView extends ViewGroup { // Icons that should go at the end if ((child == mExpandButton && mShowExpandButtonAtEnd) || child == mProfileBadge - || child == mAppOps || child == mFeedbackIcon || child == mTransferChip) { if (end == getMeasuredWidth()) { @@ -282,7 +277,7 @@ public class NotificationHeaderView extends ViewGroup { } private void updateTouchListener() { - if (mExpandClickListener == null && mAppOpsListener == null && mFeedbackListener == null) { + if (mExpandClickListener == null && mFeedbackListener == null) { setOnTouchListener(null); return; } @@ -291,14 +286,6 @@ public class NotificationHeaderView extends ViewGroup { } /** - * Sets onclick listener for app ops icons. - */ - public void setAppOpsOnClickListener(OnClickListener l) { - mAppOpsListener = l; - updateTouchListener(); - } - - /** * Sets onclick listener for feedback icon. */ public void setFeedbackOnClickListener(OnClickListener l) { @@ -394,7 +381,6 @@ public class NotificationHeaderView extends ViewGroup { private final ArrayList<Rect> mTouchRects = new ArrayList<>(); private Rect mExpandButtonRect; - private Rect mAppOpsRect; private Rect mFeedbackRect; private int mTouchSlop; private boolean mTrackGesture; @@ -408,9 +394,7 @@ public class NotificationHeaderView extends ViewGroup { mTouchRects.clear(); addRectAroundView(mIcon); mExpandButtonRect = addRectAroundView(mExpandButton); - mAppOpsRect = addRectAroundView(mAppOps); mFeedbackRect = addRectAroundView(mFeedbackIcon); - setTouchDelegate(new TouchDelegate(mAppOpsRect, mAppOps)); addWidthRect(); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } @@ -471,11 +455,7 @@ public class NotificationHeaderView extends ViewGroup { break; case MotionEvent.ACTION_UP: if (mTrackGesture) { - if (mAppOps.isVisibleToUser() && (mAppOpsRect.contains((int) x, (int) y) - || mAppOpsRect.contains((int) mDownX, (int) mDownY))) { - mAppOps.performClick(); - return true; - } else if (mFeedbackIcon.isVisibleToUser() + if (mFeedbackIcon.isVisibleToUser() && (mFeedbackRect.contains((int) x, (int) y)) || mFeedbackRect.contains((int) mDownX, (int) mDownY)) { mFeedbackIcon.performClick(); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 7923ff0991d8..32ee290a0f47 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -534,7 +534,8 @@ public interface WindowManager extends ViewManager { ScreenshotSource.SCREENSHOT_KEY_OTHER, ScreenshotSource.SCREENSHOT_OVERVIEW, ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS, - ScreenshotSource.SCREENSHOT_OTHER}) + ScreenshotSource.SCREENSHOT_OTHER, + ScreenshotSource.SCREENSHOT_VENDOR_GESTURE}) @interface ScreenshotSource { int SCREENSHOT_GLOBAL_ACTIONS = 0; int SCREENSHOT_KEY_CHORD = 1; @@ -542,6 +543,7 @@ public interface WindowManager extends ViewManager { int SCREENSHOT_OVERVIEW = 3; int SCREENSHOT_ACCESSIBILITY_ACTIONS = 4; int SCREENSHOT_OTHER = 5; + int SCREENSHOT_VENDOR_GESTURE = 6; } /** diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index d5d631ac1dc7..eef27262c699 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -54,7 +54,7 @@ public class BaseInputConnection implements InputConnection { /** @hide */ protected final InputMethodManager mIMM; final View mTargetView; - final boolean mDummyMode; + final boolean mFallbackMode; private Object[] mDefaultComposingSpans; @@ -64,14 +64,14 @@ public class BaseInputConnection implements InputConnection { BaseInputConnection(InputMethodManager mgr, boolean fullEditor) { mIMM = mgr; mTargetView = null; - mDummyMode = !fullEditor; + mFallbackMode = !fullEditor; } public BaseInputConnection(View targetView, boolean fullEditor) { mIMM = (InputMethodManager)targetView.getContext().getSystemService( Context.INPUT_METHOD_SERVICE); mTargetView = targetView; - mDummyMode = !fullEditor; + mFallbackMode = !fullEditor; } public static final void removeComposingSpans(Spannable text) { @@ -189,7 +189,7 @@ public class BaseInputConnection implements InputConnection { /** * Default implementation replaces any existing composing text with - * the given text. In addition, only if dummy mode, a key event is + * the given text. In addition, only if fallback mode, a key event is * sent for the new text and the current editable buffer cleared. */ public boolean commitText(CharSequence text, int newCursorPosition) { @@ -445,7 +445,7 @@ public class BaseInputConnection implements InputConnection { /** * The default implementation removes the composing state from the - * current editable text. In addition, only if dummy mode, a key event is + * current editable text. In addition, only if fallback mode, a key event is * sent for the new text and the current editable buffer cleared. */ public boolean finishComposingText() { @@ -454,7 +454,7 @@ public class BaseInputConnection implements InputConnection { if (content != null) { beginBatchEdit(); removeComposingSpans(content); - // Note: sendCurrentText does nothing unless mDummyMode is set + // Note: sendCurrentText does nothing unless mFallbackMode is set sendCurrentText(); endBatchEdit(); } @@ -464,10 +464,10 @@ public class BaseInputConnection implements InputConnection { /** * The default implementation uses TextUtils.getCapsMode to get the * cursor caps mode for the current selection position in the editable - * text, unless in dummy mode in which case 0 is always returned. + * text, unless in fallback mode in which case 0 is always returned. */ public int getCursorCapsMode(int reqModes) { - if (mDummyMode) return 0; + if (mFallbackMode) return 0; final Editable content = getEditable(); if (content == null) return 0; @@ -664,7 +664,7 @@ public class BaseInputConnection implements InputConnection { content.setSpan(COMPOSING, a, b, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING); - // Note: sendCurrentText does nothing unless mDummyMode is set + // Note: sendCurrentText does nothing unless mFallbackMode is set sendCurrentText(); endBatchEdit(); } @@ -715,7 +715,7 @@ public class BaseInputConnection implements InputConnection { } private void sendCurrentText() { - if (!mDummyMode) { + if (!mFallbackMode) { return; } diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index 28644858377a..7cc347d25458 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -294,7 +294,7 @@ public final class InputMethodInfo implements Parcelable { */ public InputMethodInfo(String packageName, String className, CharSequence label, String settingsActivity) { - this(buildDummyResolveInfo(packageName, className, label), false /* isAuxIme */, + this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */, settingsActivity, null /* subtypes */, 0 /* isDefaultResId */, false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */, false /* isVrOnly */); @@ -344,7 +344,7 @@ public final class InputMethodInfo implements Parcelable { mIsVrOnly = isVrOnly; } - private static ResolveInfo buildDummyResolveInfo(String packageName, String className, + private static ResolveInfo buildFakeResolveInfo(String packageName, String className, CharSequence label) { ResolveInfo ri = new ResolveInfo(); ServiceInfo si = new ServiceInfo(); diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java index 556b24c94b36..2e5ee041e54c 100644 --- a/core/java/android/webkit/UserPackage.java +++ b/core/java/android/webkit/UserPackage.java @@ -99,7 +99,7 @@ public class UserPackage { private static List<UserInfo> getAllUsers(Context context) { UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); - return userManager.getUsers(false); + return userManager.getUsers(); } } diff --git a/core/java/android/widget/inline/InlinePresentationSpec.java b/core/java/android/widget/inline/InlinePresentationSpec.java index 5f924c6ae194..e7727fd9ff1d 100644 --- a/core/java/android/widget/inline/InlinePresentationSpec.java +++ b/core/java/android/widget/inline/InlinePresentationSpec.java @@ -42,8 +42,13 @@ public final class InlinePresentationSpec implements Parcelable { private final Size mMaxSize; /** - * The extras encoding the UI style information. Defaults to {@code Bundle.Empty} in which case - * the default system UI style will be used. + * The extras encoding the UI style information. + * + * <p>The style bundles can be created using the relevant Style classes and their builders in + * the androidx autofill library e.g. {@code androidx.autofill.inline.UiVersions.StylesBuilder}. + * </p> + * + * <p>The style must be set for the suggestion to render properly.</p> * * <p>Note: There should be no remote objects in the bundle, all included remote objects will * be removed from the bundle before transmission.</p> @@ -123,8 +128,13 @@ public final class InlinePresentationSpec implements Parcelable { } /** - * The extras encoding the UI style information. Defaults to {@code Bundle.Empty} in which case - * the default system UI style will be used. + * The extras encoding the UI style information. + * + * <p>The style bundles can be created using the relevant Style classes and their builders in + * the androidx autofill library e.g. {@code androidx.autofill.inline.UiVersions.StylesBuilder}. + * </p> + * + * <p>The style must be set for the suggestion to render properly.</p> * * <p>Note: There should be no remote objects in the bundle, all included remote objects will * be removed from the bundle before transmission.</p> @@ -264,8 +274,13 @@ public final class InlinePresentationSpec implements Parcelable { } /** - * The extras encoding the UI style information. Defaults to {@code Bundle.Empty} in which case - * the default system UI style will be used. + * The extras encoding the UI style information. + * + * <p>The style bundles can be created using the relevant Style classes and their builders in + * the androidx autofill library e.g. {@code androidx.autofill.inline.UiVersions.StylesBuilder}. + * </p> + * + * <p>The style must be set for the suggestion to render properly.</p> * * <p>Note: There should be no remote objects in the bundle, all included remote objects will * be removed from the bundle before transmission.</p> @@ -302,7 +317,7 @@ public final class InlinePresentationSpec implements Parcelable { } @DataClass.Generated( - time = 1588109681295L, + time = 1596485189661L, codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/widget/inline/InlinePresentationSpec.java", inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.NonNull android.os.Bundle mStyle\nprivate static @android.annotation.NonNull android.os.Bundle defaultStyle()\nprivate boolean styleEquals(android.os.Bundle)\npublic void filterContentTypes()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []") diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java index 330c15cc4614..c82a6564c1d7 100644 --- a/core/java/com/android/internal/util/Preconditions.java +++ b/core/java/com/android/internal/util/Preconditions.java @@ -187,6 +187,32 @@ public class Preconditions { } /** + * Ensures the truth of an expression involving whether the calling identity is authorized to + * call the calling method. + * + * @param expression a boolean expression + * @throws SecurityException if {@code expression} is false + */ + public static void checkCallAuthorization(final boolean expression) { + if (!expression) { + throw new SecurityException("Calling identity is not authorized"); + } + } + + /** + * Ensures the truth of an expression involving whether the calling user is authorized to + * call the calling method. + * + * @param expression a boolean expression + * @throws SecurityException if {@code expression} is false + */ + public static void checkCallingUser(final boolean expression) { + if (!expression) { + throw new SecurityException("Calling user is not authorized"); + } + } + + /** * Check the requested flags, throwing if any requested flags are outside * the allowed set. * diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java index 9bf05135c4c5..a23fc4b57b45 100644 --- a/core/java/com/android/internal/util/ScreenshotHelper.java +++ b/core/java/com/android/internal/util/ScreenshotHelper.java @@ -291,7 +291,7 @@ public class ScreenshotHelper { }; Message msg = Message.obtain(null, screenshotType, screenshotRequest); - final ServiceConnection myConn = mScreenshotConnection; + Handler h = new Handler(handler.getLooper()) { @Override public void handleMessage(Message msg) { @@ -304,8 +304,8 @@ public class ScreenshotHelper { break; case SCREENSHOT_MSG_PROCESS_COMPLETE: synchronized (mScreenshotLock) { - if (myConn != null && mScreenshotConnection == myConn) { - mContext.unbindService(myConn); + if (mScreenshotConnection != null) { + mContext.unbindService(mScreenshotConnection); mScreenshotConnection = null; mScreenshotService = null; } @@ -368,6 +368,7 @@ public class ScreenshotHelper { } } else { Messenger messenger = new Messenger(mScreenshotService); + try { messenger.send(msg); } catch (RemoteException e) { diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index 3332143251fc..289a36f5380d 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -168,8 +168,6 @@ public class ConversationLayout extends FrameLayout private int mFacePileProtectionWidthExpanded; private boolean mImportantConversation; private TextView mUnreadBadge; - private ViewGroup mAppOps; - private Rect mAppOpsTouchRect = new Rect(); private View mFeedbackIcon; private float mMinTouchSize; private Icon mConversationIcon; @@ -214,7 +212,6 @@ public class ConversationLayout extends FrameLayout mConversationIconView = findViewById(R.id.conversation_icon); mConversationIconContainer = findViewById(R.id.conversation_icon_container); mIcon = findViewById(R.id.icon); - mAppOps = findViewById(com.android.internal.R.id.app_ops); mFeedbackIcon = findViewById(com.android.internal.R.id.feedback); mMinTouchSize = 48 * getResources().getDisplayMetrics().density; mImportanceRingView = findViewById(R.id.conversation_icon_badge_ring); @@ -1174,43 +1171,6 @@ public class ConversationLayout extends FrameLayout }); } mTouchDelegate.clear(); - if (mAppOps.getWidth() > 0) { - - // Let's increase the touch size of the app ops view if it's here - mAppOpsTouchRect.set( - mAppOps.getLeft(), - mAppOps.getTop(), - mAppOps.getRight(), - mAppOps.getBottom()); - for (int i = 0; i < mAppOps.getChildCount(); i++) { - View child = mAppOps.getChildAt(i); - if (child.getVisibility() == GONE) { - continue; - } - // Make sure each child has at least a minTouchSize touch target around it - float childTouchLeft = child.getLeft() + child.getWidth() / 2.0f - - mMinTouchSize / 2.0f; - float childTouchRight = childTouchLeft + mMinTouchSize; - mAppOpsTouchRect.left = (int) Math.min(mAppOpsTouchRect.left, - mAppOps.getLeft() + childTouchLeft); - mAppOpsTouchRect.right = (int) Math.max(mAppOpsTouchRect.right, - mAppOps.getLeft() + childTouchRight); - } - - // Increase the height - int heightIncrease = 0; - if (mAppOpsTouchRect.height() < mMinTouchSize) { - heightIncrease = (int) Math.ceil((mMinTouchSize - mAppOpsTouchRect.height()) - / 2.0f); - } - mAppOpsTouchRect.inset(0, -heightIncrease); - - getRelativeTouchRect(mAppOpsTouchRect, mAppOps); - - // Extend the size of the app opps to be at least 48dp - mTouchDelegate.add(new TouchDelegate(mAppOpsTouchRect, mAppOps)); - - } if (mFeedbackIcon.getVisibility() == VISIBLE) { updateFeedbackIconMargins(); float width = Math.max(mMinTouchSize, mFeedbackIcon.getWidth()); @@ -1240,13 +1200,7 @@ public class ConversationLayout extends FrameLayout private void updateFeedbackIconMargins() { MarginLayoutParams lp = (MarginLayoutParams) mFeedbackIcon.getLayoutParams(); - if (mAppOps.getWidth() == 0) { - lp.setMarginStart(mNotificationHeaderSeparatingMargin); - } else { - float width = Math.max(mMinTouchSize, mFeedbackIcon.getWidth()); - int horizontalMargin = (int) ((width - mFeedbackIcon.getWidth()) / 2); - lp.setMarginStart(horizontalMargin); - } + lp.setMarginStart(mNotificationHeaderSeparatingMargin); mFeedbackIcon.setLayoutParams(lp); } diff --git a/core/jni/android_media_AudioDeviceAttributes.cpp b/core/jni/android_media_AudioDeviceAttributes.cpp index e79c95edbeb5..2a16dce99125 100644 --- a/core/jni/android_media_AudioDeviceAttributes.cpp +++ b/core/jni/android_media_AudioDeviceAttributes.cpp @@ -31,7 +31,7 @@ jint createAudioDeviceAttributesFromNative(JNIEnv *env, jobject *jAudioDeviceAtt const AudioDeviceTypeAddr *devTypeAddr) { jint jStatus = (jint)AUDIO_JAVA_SUCCESS; jint jNativeType = (jint)devTypeAddr->mType; - ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->mAddress.data())); + ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->getAddress())); *jAudioDeviceAttributes = env->NewObject(gAudioDeviceAttributesClass, gAudioDeviceAttributesCstor, jNativeType, jAddress.get()); diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 1f625443c96e..3f39478ffd43 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -172,6 +172,8 @@ static struct { jmethodID postRecordConfigEventFromNative; } gAudioPolicyEventHandlerMethods; +static struct { jmethodID add; } gListMethods; + // // JNI Initialization for OpenSLES routing // @@ -310,7 +312,7 @@ static int _check_AudioSystem_Command(const char* caller, status_t status) static jint getVectorOfAudioDeviceTypeAddr(JNIEnv *env, jintArray deviceTypes, jobjectArray deviceAddresses, - Vector<AudioDeviceTypeAddr> &audioDeviceTypeAddrVector) { + AudioDeviceTypeAddrVector &audioDeviceTypeAddrVector) { if (deviceTypes == nullptr || deviceAddresses == nullptr) { return (jint)AUDIO_JAVA_BAD_VALUE; } @@ -337,7 +339,7 @@ static jint getVectorOfAudioDeviceTypeAddr(JNIEnv *env, jintArray deviceTypes, } const char *address = env->GetStringUTFChars((jstring)addrJobj, NULL); AudioDeviceTypeAddr dev = AudioDeviceTypeAddr(typesPtr[i], address); - audioDeviceTypeAddrVector.add(dev); + audioDeviceTypeAddrVector.push_back(dev); env->ReleaseStringUTFChars((jstring)addrJobj, address); } env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0); @@ -2062,7 +2064,7 @@ exit: static jint android_media_AudioSystem_setUidDeviceAffinities(JNIEnv *env, jobject clazz, jint uid, jintArray deviceTypes, jobjectArray deviceAddresses) { - Vector<AudioDeviceTypeAddr> deviceVector; + AudioDeviceTypeAddrVector deviceVector; jint results = getVectorOfAudioDeviceTypeAddr(env, deviceTypes, deviceAddresses, deviceVector); if (results != NO_ERROR) { return results; @@ -2080,7 +2082,7 @@ static jint android_media_AudioSystem_removeUidDeviceAffinities(JNIEnv *env, job static jint android_media_AudioSystem_setUserIdDeviceAffinities(JNIEnv *env, jobject clazz, jint userId, jintArray deviceTypes, jobjectArray deviceAddresses) { - Vector<AudioDeviceTypeAddr> deviceVector; + AudioDeviceTypeAddrVector deviceVector; jint results = getVectorOfAudioDeviceTypeAddr(env, deviceTypes, deviceAddresses, deviceVector); if (results != NO_ERROR) { return results; @@ -2361,46 +2363,48 @@ android_media_AudioSystem_isCallScreeningModeSupported(JNIEnv *env, jobject thiz return AudioSystem::isCallScreenModeSupported(); } -static jint -android_media_AudioSystem_setPreferredDeviceForStrategy(JNIEnv *env, jobject thiz, - jint strategy, jint deviceType, jstring deviceAddress) { - - const char *c_address = env->GetStringUTFChars(deviceAddress, NULL); +static jint android_media_AudioSystem_setDevicesRoleForStrategy(JNIEnv *env, jobject thiz, + jint strategy, jint role, + jintArray jDeviceTypes, + jobjectArray jDeviceAddresses) { + AudioDeviceTypeAddrVector nDevices; + jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices); + if (results != NO_ERROR) { + return results; + } int status = check_AudioSystem_Command( - AudioSystem::setPreferredDeviceForStrategy((product_strategy_t) strategy, - AudioDeviceTypeAddr(deviceType, c_address))); - env->ReleaseStringUTFChars(deviceAddress, c_address); + AudioSystem::setDevicesRoleForStrategy((product_strategy_t)strategy, + (device_role_t)role, nDevices)); return (jint) status; } -static jint -android_media_AudioSystem_removePreferredDeviceForStrategy(JNIEnv *env, jobject thiz, jint strategy) -{ - return (jint) check_AudioSystem_Command( - AudioSystem::removePreferredDeviceForStrategy((product_strategy_t) strategy)); +static jint android_media_AudioSystem_removeDevicesRoleForStrategy(JNIEnv *env, jobject thiz, + jint strategy, jint role) { + return (jint)check_AudioSystem_Command( + AudioSystem::removeDevicesRoleForStrategy((product_strategy_t)strategy, + (device_role_t)role)); } -static jint -android_media_AudioSystem_getPreferredDeviceForStrategy(JNIEnv *env, jobject thiz, - jint strategy, jobjectArray jDeviceArray) -{ - if (jDeviceArray == nullptr || env->GetArrayLength(jDeviceArray) != 1) { - ALOGE("%s invalid array to store AudioDeviceAttributes", __FUNCTION__); - return (jint)AUDIO_JAVA_BAD_VALUE; - } - - AudioDeviceTypeAddr elDevice; +static jint android_media_AudioSystem_getDevicesForRoleAndStrategy(JNIEnv *env, jobject thiz, + jint strategy, jint role, + jobject jDevices) { + AudioDeviceTypeAddrVector nDevices; status_t status = check_AudioSystem_Command( - AudioSystem::getPreferredDeviceForStrategy((product_strategy_t) strategy, elDevice)); + AudioSystem::getDevicesForRoleAndStrategy((product_strategy_t)strategy, + (device_role_t)role, nDevices)); if (status != NO_ERROR) { return (jint) status; } - jobject jAudioDeviceAttributes = NULL; - jint jStatus = createAudioDeviceAttributesFromNative(env, &jAudioDeviceAttributes, &elDevice); - if (jStatus == AUDIO_JAVA_SUCCESS) { - env->SetObjectArrayElement(jDeviceArray, 0, jAudioDeviceAttributes); + for (const auto &device : nDevices) { + jobject jAudioDeviceAttributes = NULL; + jint jStatus = createAudioDeviceAttributesFromNative(env, &jAudioDeviceAttributes, &device); + if (jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; + } + env->CallBooleanMethod(jDevices, gListMethods.add, jAudioDeviceAttributes); + env->DeleteLocalRef(jAudioDeviceAttributes); } - return jStatus; + return AUDIO_JAVA_SUCCESS; } static jint @@ -2548,12 +2552,12 @@ static const JNINativeMethod gMethods[] = {"setAudioHalPids", "([I)I", (void *)android_media_AudioSystem_setAudioHalPids}, {"isCallScreeningModeSupported", "()Z", (void *)android_media_AudioSystem_isCallScreeningModeSupported}, - {"setPreferredDeviceForStrategy", "(IILjava/lang/String;)I", - (void *)android_media_AudioSystem_setPreferredDeviceForStrategy}, - {"removePreferredDeviceForStrategy", "(I)I", - (void *)android_media_AudioSystem_removePreferredDeviceForStrategy}, - {"getPreferredDeviceForStrategy", "(I[Landroid/media/AudioDeviceAttributes;)I", - (void *)android_media_AudioSystem_getPreferredDeviceForStrategy}, + {"setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", + (void *)android_media_AudioSystem_setDevicesRoleForStrategy}, + {"removeDevicesRoleForStrategy", "(II)I", + (void *)android_media_AudioSystem_removeDevicesRoleForStrategy}, + {"getDevicesForRoleAndStrategy", "(IILjava/util/List;)I", + (void *)android_media_AudioSystem_getDevicesForRoleAndStrategy}, {"getDevicesForAttributes", "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;)I", (void *)android_media_AudioSystem_getDevicesForAttributes}, @@ -2755,6 +2759,9 @@ int register_android_media_AudioSystem(JNIEnv *env) gMidAudioRecordRoutingProxy_release = android::GetMethodIDOrDie(env, gClsAudioRecordRoutingProxy, "native_release", "()V"); + jclass listClass = FindClassOrDie(env, "java/util/List"); + gListMethods.add = GetMethodIDOrDie(env, listClass, "add", "(Ljava/lang/Object;)Z"); + AudioSystem::addErrorCallback(android_media_AudioSystem_error_callback); RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index c73441cbd4f9..f96ed36fcedd 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -1790,8 +1790,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, #ifdef ANDROID_EXPERIMENTAL_MTE SetTagCheckingLevel(PR_MTE_TCF_SYNC); #endif - // TODO(pcc): Use SYNC here once the allocator supports it. - heap_tagging_level = M_HEAP_TAGGING_LEVEL_ASYNC; + heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC; break; default: #ifdef ANDROID_EXPERIMENTAL_MTE diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 1014fbb73877..6eb89040998a 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -744,6 +744,41 @@ enum Action { // CATEGORY: SETTINGS // OS: R ACTION_CONFIRM_SIM_DELETION_OFF = 1739; + + // ACTION: Settings > System > Gestures > Double tap > Toggle on Double tap + // CATEGORY: SETTINGS + // OS: S + ACTION_COLUMBUS_ENABLED = 1740; + + // ACTION: Settings > System > Gestures > Double tap > Toggle off Double tap + // CATEGORY: SETTINGS + // OS: S + ACTION_COLUMBUS_DISABLED = 1741; + + // ACTION: Settings > System > Gestures > Double tap > Invoke Assistant + // CATEGORY: SETTINGS + // OS: S + ACTION_COLUMBUS_ACTION_ASSISTANT = 1742; + + // ACTION: Settings > System > Gestures > Double tap > Take screenshot + // CATEGORY: SETTINGS + // OS: S + ACTION_COLUMBUS_ACTION_SCREENSHOT = 1743; + + // ACTION: Settings > System > Gestures > Double tap > Play and pause + // CATEGORY: SETTINGS + // OS: S + ACTION_COLUMBUS_ACTION_PLAY_PAUSE = 1744; + + // ACTION: Settings > System > Gestures > Double tap > Open app overview + // CATEGORY: SETTINGS + // OS: S + ACTION_COLUMBUS_ACTION_OVERVIEW = 1745; + + // ACTION: Settings > System > Gestures > Double tap > Open notification shade + // CATEGORY: SETTINGS + // OS: S + ACTION_COLUMBUS_ACTION_NOTIFICATION_SHADE = 1746; } /** @@ -2310,10 +2345,10 @@ enum PageId { // OS: Q ZEN_CUSTOM_SETTINGS_DIALOG = 1612; - // OPEN: Settings > Developer Options > Game Driver Preferences + // OPEN: Settings > Developer Options > Graphics Driver Preferences // CATEGORY: SETTINGS // OS: Q - SETTINGS_GAME_DRIVER_DASHBOARD = 1613; + SETTINGS_GRAPHICS_DRIVER_DASHBOARD = 1613; // OPEN: Settings > Accessibility > Vibration > Ring vibration // CATEGORY: SETTINGS diff --git a/core/proto/android/app/tvsettings_enums.proto b/core/proto/android/app/tvsettings_enums.proto index 31c5dd6b730a..4a3c59477f2f 100644 --- a/core/proto/android/app/tvsettings_enums.proto +++ b/core/proto/android/app/tvsettings_enums.proto @@ -168,7 +168,11 @@ enum ItemId { // Google Assistant > Personal results (toggle) ACCOUNT_SLICE_REG_ACCOUNT_ASSISTANT_PERSONAL_RESULTS = 0x12134000; - // Reserving [0x12140000, 0x12190000] for possible future settings + // TvSettings > Account & Sign In (Slice) > [A regular account] > + // Apps only mode (toggle) + ACCOUNT_SLICE_REG_ACCOUNT_APPS_ONLY_MODE = 0x12140000; + + // Reserving [0x12150000, 0x12190000] for possible future settings // TvSettings > Account & Sign In (Slice) > [A regular account] > Remove ACCOUNT_SLICE_REG_ACCOUNT_REMOVE = 0x121A0000; diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index e4a142b3335b..d5619ca96fbd 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -433,35 +433,36 @@ message GlobalSettingsProto { // Ordered GPU debug layer list for GLES // i.e. <layer1>:<layer2>:...:<layerN> optional SettingProto debug_layers_gles = 7; - // Game Driver - global preference for all Apps + // Updatable Driver - global preference for all Apps // 0 = Default - // 1 = All Apps use Game Driver - // 2 = All Apps use system graphics driver - optional SettingProto game_driver_all_apps = 8; - // Game Driver - List of Apps selected to use Game Driver + // 1 = All Apps use updatable production driver + // 2 = All apps use updatable prerelease driver + // 3 = All Apps use system graphics driver + optional SettingProto updatable_driver_all_apps = 8; + // Updatable Driver - List of Apps selected to use updatable production driver // i.e. <pkg1>,<pkg2>,...,<pkgN> - optional SettingProto game_driver_opt_in_apps = 9; - // Game Driver - List of Apps selected not to use Game Driver + optional SettingProto updatable_driver_production_opt_in_apps = 9; + // Updatable Driver - List of Apps selected not to use updatable production driver // i.e. <pkg1>,<pkg2>,...,<pkgN> - optional SettingProto game_driver_opt_out_apps = 10; - // Game Driver - List of Apps that are forbidden to use Game Driver - optional SettingProto game_driver_denylist = 11; - // Game Driver - List of Apps that are allowed to use Game Driver - optional SettingProto game_driver_allowlist = 12; + optional SettingProto updatable_driver_production_opt_out_apps = 10; + // Updatable Driver - List of Apps that are forbidden to use updatable production driver + optional SettingProto updatable_driver_production_denylist = 11; + // Updatable Driver - List of Apps that are allowed to use updatable production driver + optional SettingProto updatable_driver_production_allowlist = 12; // ANGLE - List of Apps that can check ANGLE rules optional SettingProto angle_allowlist = 13; - // Game Driver - List of denylists, each denylist is a denylist for - // a specific Game Driver version - optional SettingProto game_driver_denylists = 14; + // Updatable Driver - List of denylists, each denylist is a denylist for + // a specific updatable production driver version + optional SettingProto updatable_driver_production_denylists = 14; // ANGLE - Show a dialog box when ANGLE is selected for the currently running PKG optional SettingProto show_angle_in_use_dialog = 15; - // Game Driver - List of libraries in sphal accessible by Game Driver - optional SettingProto game_driver_sphal_libraries = 16; + // Updatable Driver - List of libraries in sphal accessible by updatable driver + optional SettingProto updatable_driver_sphal_libraries = 16; // ANGLE - External package containing ANGLE libraries optional SettingProto angle_debug_package = 17; - // Game Driver - List of Apps selected to use prerelease Game Driver + // Updatable Driver - List of Apps selected to use updatable prerelease driver // i.e. <pkg1>,<pkg2>,...,<pkgN> - optional SettingProto game_driver_prerelease_opt_in_apps = 18; + optional SettingProto updatable_driver_prerelease_opt_in_apps = 18; } optional Gpu gpu = 59; diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index ca4dc18689bc..3d12d072eb80 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -181,6 +181,7 @@ message SecureSettingsProto { optional SettingProto cmas_additional_broadcast_pkg = 14 [ (android.privacy).dest = DEST_AUTOMATIC ]; repeated SettingProto completed_categories = 15; optional SettingProto connectivity_release_pending_intent_delay_ms = 16 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto adaptive_connectivity_enabled = 84 [ (android.privacy).dest = DEST_AUTOMATIC ]; message Controls { option (android.msg_privacy).dest = DEST_EXPLICIT; @@ -211,6 +212,7 @@ message SecureSettingsProto { message EmergencyResponse { optional SettingProto panic_gesture_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto panic_sound_enabled = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional EmergencyResponse emergency_response = 83; @@ -613,5 +615,5 @@ message SecureSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 84; + // Next tag = 85; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index fe290f3e97e8..32c1e4a1411c 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -558,6 +558,7 @@ <protected-broadcast android:name="android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED" /> <protected-broadcast android:name="android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED" /> <protected-broadcast android:name="android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED" /> + <protected-broadcast android:name="android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED" /> <protected-broadcast android:name="android.app.action.APP_BLOCK_STATE_CHANGED" /> <protected-broadcast android:name="android.permission.GET_APP_GRANTED_URI_PERMISSIONS" /> @@ -1342,7 +1343,7 @@ android:priority="800" /> <!-- Allows an application to access data from sensors that the user uses to - measure what is happening inside his/her body, such as heart rate. + measure what is happening inside their body, such as heart rate. <p>Protection level: dangerous --> <permission android:name="android.permission.BODY_SENSORS" android:permissionGroup="android.permission-group.UNDEFINED" diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index d22a19faa52b..9a1b592c895a 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -160,43 +160,6 @@ android:visibility="gone" android:contentDescription="@string/notification_work_profile_content_description" /> - <LinearLayout - android:id="@+id/app_ops" - android:layout_height="match_parent" - android:layout_width="wrap_content" - android:layout_marginStart="6dp" - android:background="?android:selectableItemBackgroundBorderless" - android:orientation="horizontal"> - <ImageView - android:id="@+id/camera" - android:layout_width="?attr/notificationHeaderIconSize" - android:layout_height="?attr/notificationHeaderIconSize" - android:src="@drawable/ic_camera" - android:visibility="gone" - android:focusable="false" - android:contentDescription="@string/notification_appops_camera_active" - /> - <ImageView - android:id="@+id/mic" - android:layout_width="?attr/notificationHeaderIconSize" - android:layout_height="?attr/notificationHeaderIconSize" - android:src="@drawable/ic_mic" - android:layout_marginStart="4dp" - android:visibility="gone" - android:focusable="false" - android:contentDescription="@string/notification_appops_microphone_active" - /> - <ImageView - android:id="@+id/overlay" - android:layout_width="?attr/notificationHeaderIconSize" - android:layout_height="?attr/notificationHeaderIconSize" - android:src="@drawable/ic_alert_window_layer" - android:layout_marginStart="4dp" - android:visibility="gone" - android:focusable="false" - android:contentDescription="@string/notification_appops_overlay_active" - /> - </LinearLayout> <include layout="@layout/notification_material_media_transfer_action" android:id="@+id/media_seamless" diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index d0ed635e69a8..bad330e75864 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -983,9 +983,9 @@ <string name="menu_space_shortcut_label" msgid="5949311515646872071">"স্পেচ"</string> <string name="menu_enter_shortcut_label" msgid="6709499510082897320">"লিখক"</string> <string name="menu_delete_shortcut_label" msgid="4365787714477739080">"মচক"</string> - <string name="search_go" msgid="2141477624421347086">"অনুসন্ধান কৰক"</string> + <string name="search_go" msgid="2141477624421347086">"Search"</string> <string name="search_hint" msgid="455364685740251925">"অনুসন্ধান কৰক…"</string> - <string name="searchview_description_search" msgid="1045552007537359343">"অনুসন্ধান কৰক"</string> + <string name="searchview_description_search" msgid="1045552007537359343">"Search"</string> <string name="searchview_description_query" msgid="7430242366971716338">"প্ৰশ্নৰ সন্ধান কৰক"</string> <string name="searchview_description_clear" msgid="1989371719192982900">"প্ৰশ্ন মচক"</string> <string name="searchview_description_submit" msgid="6771060386117334686">"প্ৰশ্ন দাখিল কৰক"</string> @@ -1401,7 +1401,7 @@ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"জুম নিয়ন্ত্ৰণ কৰিবলৈ দুবাৰ টিপক"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"ৱিজেট যোগ কৰিব পৰা নগ\'ল।"</string> <string name="ime_action_go" msgid="5536744546326495436">"যাওক"</string> - <string name="ime_action_search" msgid="4501435960587287668">"অনুসন্ধান কৰক"</string> + <string name="ime_action_search" msgid="4501435960587287668">"Search"</string> <string name="ime_action_send" msgid="8456843745664334138">"পঠিয়াওক"</string> <string name="ime_action_next" msgid="4169702997635728543">"পৰৱৰ্তী"</string> <string name="ime_action_done" msgid="6299921014822891569">"সম্পন্ন হ’ল"</string> @@ -1881,7 +1881,7 @@ <string name="language_picker_section_suggested" msgid="6556199184638990447">"প্ৰস্তাৱিত"</string> <string name="language_picker_section_all" msgid="1985809075777564284">"সকলো ভাষা"</string> <string name="region_picker_section_all" msgid="756441309928774155">"সকলো অঞ্চল"</string> - <string name="locale_search_menu" msgid="6258090710176422934">"অনুসন্ধান কৰক"</string> + <string name="locale_search_menu" msgid="6258090710176422934">"Search"</string> <string name="app_suspended_title" msgid="888873445010322650">"এপটো নাই"</string> <string name="app_suspended_default_message" msgid="6451215678552004172">"এই মুহূৰ্তত <xliff:g id="APP_NAME_0">%1$s</xliff:g> উপলব্ধ নহয়। ইয়াক <xliff:g id="APP_NAME_1">%2$s</xliff:g>এ পৰিচালনা কৰে।"</string> <string name="app_suspended_more_details" msgid="211260942831587014">"অধিক জানক"</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index ffc9ecf02b18..22589cd00b06 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -983,9 +983,9 @@ <string name="menu_space_shortcut_label" msgid="5949311515646872071">"স্পেস"</string> <string name="menu_enter_shortcut_label" msgid="6709499510082897320">"enter"</string> <string name="menu_delete_shortcut_label" msgid="4365787714477739080">"মুছুন"</string> - <string name="search_go" msgid="2141477624421347086">"খুঁজুন"</string> + <string name="search_go" msgid="2141477624421347086">"সার্চ"</string> <string name="search_hint" msgid="455364685740251925">"সার্চ করুন..."</string> - <string name="searchview_description_search" msgid="1045552007537359343">"খুঁজুন"</string> + <string name="searchview_description_search" msgid="1045552007537359343">"সার্চ"</string> <string name="searchview_description_query" msgid="7430242366971716338">"সার্চ ক্যোয়ারী"</string> <string name="searchview_description_clear" msgid="1989371719192982900">"ক্যোয়ারী সাফ করুন"</string> <string name="searchview_description_submit" msgid="6771060386117334686">"ক্যোয়ারী জমা দিন"</string> @@ -1401,7 +1401,7 @@ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"জুম নিয়ন্ত্রণের জন্য দুবার ট্যাপ করুন"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"উইজেট যোগ করা যায়নি৷"</string> <string name="ime_action_go" msgid="5536744546326495436">"যান"</string> - <string name="ime_action_search" msgid="4501435960587287668">"খুঁজুন"</string> + <string name="ime_action_search" msgid="4501435960587287668">"সার্চ"</string> <string name="ime_action_send" msgid="8456843745664334138">"পাঠান"</string> <string name="ime_action_next" msgid="4169702997635728543">"পরবর্তী"</string> <string name="ime_action_done" msgid="6299921014822891569">"সম্পন্ন হয়েছে"</string> @@ -1881,7 +1881,7 @@ <string name="language_picker_section_suggested" msgid="6556199184638990447">"প্রস্তাবিত"</string> <string name="language_picker_section_all" msgid="1985809075777564284">"সকল ভাষা"</string> <string name="region_picker_section_all" msgid="756441309928774155">"সমস্ত অঞ্চল"</string> - <string name="locale_search_menu" msgid="6258090710176422934">"খুঁজুন"</string> + <string name="locale_search_menu" msgid="6258090710176422934">"সার্চ"</string> <string name="app_suspended_title" msgid="888873445010322650">"অ্যাপটি উপলভ্য নয়"</string> <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> এখন উপলভ্য নয়। এই অ্যাপটিকে <xliff:g id="APP_NAME_1">%2$s</xliff:g> অ্যাপ ম্যানেজ করে।"</string> <string name="app_suspended_more_details" msgid="211260942831587014">"আরও জানুন"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index ca35a681f636..5ffc420b13b4 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -985,7 +985,7 @@ <string name="menu_delete_shortcut_label" msgid="4365787714477739080">"ડિલીટ કરો"</string> <string name="search_go" msgid="2141477624421347086">"શોધો"</string> <string name="search_hint" msgid="455364685740251925">"શોધો…"</string> - <string name="searchview_description_search" msgid="1045552007537359343">"શોધ"</string> + <string name="searchview_description_search" msgid="1045552007537359343">"શોધો"</string> <string name="searchview_description_query" msgid="7430242366971716338">"શોધ ક્વેરી"</string> <string name="searchview_description_clear" msgid="1989371719192982900">"ક્વેરી સાફ કરો"</string> <string name="searchview_description_submit" msgid="6771060386117334686">"ક્વેરી સબમિટ કરો"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index cbb9ca2606f2..c7786aebe9e4 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -983,9 +983,9 @@ <string name="menu_space_shortcut_label" msgid="5949311515646872071">"space"</string> <string name="menu_enter_shortcut_label" msgid="6709499510082897320">"enter"</string> <string name="menu_delete_shortcut_label" msgid="4365787714477739080">"ಅಳಿಸಿ"</string> - <string name="search_go" msgid="2141477624421347086">"ಹುಡುಕಿ"</string> + <string name="search_go" msgid="2141477624421347086">"Search"</string> <string name="search_hint" msgid="455364685740251925">"ಹುಡುಕಿ…"</string> - <string name="searchview_description_search" msgid="1045552007537359343">"ಹುಡುಕಿ"</string> + <string name="searchview_description_search" msgid="1045552007537359343">"Search"</string> <string name="searchview_description_query" msgid="7430242366971716338">"ಪ್ರಶ್ನೆಯನ್ನು ಹುಡುಕಿ"</string> <string name="searchview_description_clear" msgid="1989371719192982900">"ಪ್ರಶ್ನೆಯನ್ನು ತೆರವುಗೊಳಿಸು"</string> <string name="searchview_description_submit" msgid="6771060386117334686">"ಪ್ರಶ್ನೆಯನ್ನು ಸಲ್ಲಿಸು"</string> @@ -1401,7 +1401,7 @@ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ಝೂಮ್ ನಿಯಂತ್ರಿಸಲು ಎರಡು ಬಾರಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"ವಿಜೆಟ್ ಸೇರಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ."</string> <string name="ime_action_go" msgid="5536744546326495436">"ಹೋಗು"</string> - <string name="ime_action_search" msgid="4501435960587287668">"ಹುಡುಕಿ"</string> + <string name="ime_action_search" msgid="4501435960587287668">"Search"</string> <string name="ime_action_send" msgid="8456843745664334138">"ಕಳುಹಿಸು"</string> <string name="ime_action_next" msgid="4169702997635728543">"ಮುಂದೆ"</string> <string name="ime_action_done" msgid="6299921014822891569">"ಮುಗಿದಿದೆ"</string> @@ -1881,7 +1881,7 @@ <string name="language_picker_section_suggested" msgid="6556199184638990447">"ಸೂಚಿತ ಭಾಷೆ"</string> <string name="language_picker_section_all" msgid="1985809075777564284">"ಎಲ್ಲಾ ಭಾಷೆಗಳು"</string> <string name="region_picker_section_all" msgid="756441309928774155">"ಎಲ್ಲಾ ಪ್ರದೇಶಗಳು"</string> - <string name="locale_search_menu" msgid="6258090710176422934">"ಹುಡುಕಿ"</string> + <string name="locale_search_menu" msgid="6258090710176422934">"Search"</string> <string name="app_suspended_title" msgid="888873445010322650">"ಅಪ್ಲಿಕೇಶನ್ ಲಭ್ಯವಿಲ್ಲ"</string> <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ಅಪ್ಲಿಕೇಶನ್ ಸದ್ಯಕ್ಕೆ ಲಭ್ಯವಿಲ್ಲ. ಇದನ್ನು <xliff:g id="APP_NAME_1">%2$s</xliff:g> ನಲ್ಲಿ ನಿರ್ವಹಿಸಲಾಗುತ್ತಿದೆ."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index af75969910d2..12c0cb4025ee 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -983,9 +983,9 @@ <string name="menu_space_shortcut_label" msgid="5949311515646872071">"space"</string> <string name="menu_enter_shortcut_label" msgid="6709499510082897320">"enter"</string> <string name="menu_delete_shortcut_label" msgid="4365787714477739080">"delete"</string> - <string name="search_go" msgid="2141477624421347086">"തിരയൽ"</string> + <string name="search_go" msgid="2141477624421347086">"Search"</string> <string name="search_hint" msgid="455364685740251925">"തിരയുക…"</string> - <string name="searchview_description_search" msgid="1045552007537359343">"തിരയൽ"</string> + <string name="searchview_description_search" msgid="1045552007537359343">"Search"</string> <string name="searchview_description_query" msgid="7430242366971716338">"തിരയൽ അന്വേഷണം"</string> <string name="searchview_description_clear" msgid="1989371719192982900">"അന്വേഷണം മായ്ക്കുക"</string> <string name="searchview_description_submit" msgid="6771060386117334686">"ചോദ്യം സമർപ്പിക്കുക"</string> @@ -1401,7 +1401,7 @@ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"സൂം നിയന്ത്രണം ലഭിക്കാൻ രണ്ടുതവണ ടാപ്പുചെയ്യുക"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"വിജറ്റ് ചേർക്കാനായില്ല."</string> <string name="ime_action_go" msgid="5536744546326495436">"പോവുക"</string> - <string name="ime_action_search" msgid="4501435960587287668">"തിരയൽ"</string> + <string name="ime_action_search" msgid="4501435960587287668">"Search"</string> <string name="ime_action_send" msgid="8456843745664334138">"അയയ്ക്കുക"</string> <string name="ime_action_next" msgid="4169702997635728543">"അടുത്തത്"</string> <string name="ime_action_done" msgid="6299921014822891569">"പൂർത്തിയായി"</string> @@ -1881,7 +1881,7 @@ <string name="language_picker_section_suggested" msgid="6556199184638990447">"നിര്ദ്ദേശിച്ചത്"</string> <string name="language_picker_section_all" msgid="1985809075777564284">"എല്ലാ ഭാഷകളും"</string> <string name="region_picker_section_all" msgid="756441309928774155">"എല്ലാ പ്രദേശങ്ങളും"</string> - <string name="locale_search_menu" msgid="6258090710176422934">"തിരയുക"</string> + <string name="locale_search_menu" msgid="6258090710176422934">"Search"</string> <string name="app_suspended_title" msgid="888873445010322650">"ആപ്പ് ലഭ്യമല്ല"</string> <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ഇപ്പോൾ ലഭ്യമല്ല. <xliff:g id="APP_NAME_1">%2$s</xliff:g> ആണ് ഇത് മാനേജ് ചെയ്യുന്നത്."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"കൂടുതലറിയുക"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index ec240c1c36fc..da6f2b71c65a 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -983,9 +983,9 @@ <string name="menu_space_shortcut_label" msgid="5949311515646872071">"स्पेस"</string> <string name="menu_enter_shortcut_label" msgid="6709499510082897320">"एंटर"</string> <string name="menu_delete_shortcut_label" msgid="4365787714477739080">"हटवा"</string> - <string name="search_go" msgid="2141477624421347086">"शोध"</string> + <string name="search_go" msgid="2141477624421347086">"Search"</string> <string name="search_hint" msgid="455364685740251925">"शोधा…"</string> - <string name="searchview_description_search" msgid="1045552007537359343">"शोध"</string> + <string name="searchview_description_search" msgid="1045552007537359343">"Search"</string> <string name="searchview_description_query" msgid="7430242366971716338">"शोध क्वेरी"</string> <string name="searchview_description_clear" msgid="1989371719192982900">"क्वेरी साफ करा"</string> <string name="searchview_description_submit" msgid="6771060386117334686">"क्वेरी सबमिट करा"</string> @@ -1401,7 +1401,7 @@ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"झूम नियंत्रणासाठी दोनदा टॅप करा"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"विजेट जोडू शकलो नाही."</string> <string name="ime_action_go" msgid="5536744546326495436">"जा"</string> - <string name="ime_action_search" msgid="4501435960587287668">"शोधा"</string> + <string name="ime_action_search" msgid="4501435960587287668">"Search"</string> <string name="ime_action_send" msgid="8456843745664334138">"पाठवा"</string> <string name="ime_action_next" msgid="4169702997635728543">"पुढे"</string> <string name="ime_action_done" msgid="6299921014822891569">"पूर्ण झाले"</string> @@ -1881,7 +1881,7 @@ <string name="language_picker_section_suggested" msgid="6556199184638990447">"सुचवलेल्या भाषा"</string> <string name="language_picker_section_all" msgid="1985809075777564284">"सर्व भाषा"</string> <string name="region_picker_section_all" msgid="756441309928774155">"सर्व प्रदेश"</string> - <string name="locale_search_menu" msgid="6258090710176422934">"शोध"</string> + <string name="locale_search_menu" msgid="6258090710176422934">"Search"</string> <string name="app_suspended_title" msgid="888873445010322650">"अॅप उपलब्ध नाही"</string> <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> आत्ता उपलब्ध नाही. हे <xliff:g id="APP_NAME_1">%2$s</xliff:g> कडून व्यवस्थापित केले जाते."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"अधिक जाणून घ्या"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index a1fd3f022555..1cb01ead277b 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -983,9 +983,9 @@ <string name="menu_space_shortcut_label" msgid="5949311515646872071">"ସ୍ପେସ୍"</string> <string name="menu_enter_shortcut_label" msgid="6709499510082897320">"ଏଣ୍ଟର୍"</string> <string name="menu_delete_shortcut_label" msgid="4365787714477739080">"ଡିଲିଟ୍"</string> - <string name="search_go" msgid="2141477624421347086">"ସର୍ଚ୍ଚ କରନ୍ତୁ"</string> + <string name="search_go" msgid="2141477624421347086">"Search"</string> <string name="search_hint" msgid="455364685740251925">"ସର୍ଚ୍ଚ…"</string> - <string name="searchview_description_search" msgid="1045552007537359343">"ସର୍ଚ୍ଚ କରନ୍ତୁ"</string> + <string name="searchview_description_search" msgid="1045552007537359343">"Search"</string> <string name="searchview_description_query" msgid="7430242366971716338">"କ୍ୱେରୀ ସର୍ଚ୍ଚ କରନ୍ତୁ"</string> <string name="searchview_description_clear" msgid="1989371719192982900">"କ୍ୱେରୀ ଖାଲି କରନ୍ତୁ"</string> <string name="searchview_description_submit" msgid="6771060386117334686">"କ୍ୱେରୀ ଦାଖଲ କରନ୍ତୁ"</string> @@ -1401,7 +1401,7 @@ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ଜୁମ୍ ନିୟନ୍ତ୍ରଣ ପାଇଁ ଦୁଇଥର ଟାପ୍ କରନ୍ତୁ"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"ୱିଜେଟ୍ ଯୋଡ଼ିପାରିବ ନାହିଁ।"</string> <string name="ime_action_go" msgid="5536744546326495436">"ଯାଆନ୍ତୁ"</string> - <string name="ime_action_search" msgid="4501435960587287668">"ସର୍ଚ୍ଚ କରନ୍ତୁ"</string> + <string name="ime_action_search" msgid="4501435960587287668">"Search"</string> <string name="ime_action_send" msgid="8456843745664334138">"ପଠାନ୍ତୁ"</string> <string name="ime_action_next" msgid="4169702997635728543">"ପରବର୍ତ୍ତୀ"</string> <string name="ime_action_done" msgid="6299921014822891569">"ହୋଇଗଲା"</string> @@ -1881,7 +1881,7 @@ <string name="language_picker_section_suggested" msgid="6556199184638990447">"ପ୍ରସ୍ତାବିତ"</string> <string name="language_picker_section_all" msgid="1985809075777564284">"ସମସ୍ତ ଭାଷା"</string> <string name="region_picker_section_all" msgid="756441309928774155">"ସମସ୍ତ ଅଞ୍ଚଳ"</string> - <string name="locale_search_menu" msgid="6258090710176422934">"ସର୍ଚ୍ଚ କରନ୍ତୁ"</string> + <string name="locale_search_menu" msgid="6258090710176422934">"Search"</string> <string name="app_suspended_title" msgid="888873445010322650">"ଆପ୍ ଉପଲବ୍ଧ ନାହିଁ"</string> <string name="app_suspended_default_message" msgid="6451215678552004172">"ବର୍ତ୍ତମାନ <xliff:g id="APP_NAME_0">%1$s</xliff:g> ଉପଲବ୍ଧ ନାହିଁ। ଏହା <xliff:g id="APP_NAME_1">%2$s</xliff:g> ଦ୍ଵାରା ପରିଚାଳିତ ହେଉଛି।"</string> <string name="app_suspended_more_details" msgid="211260942831587014">"ଅଧିକ ଜାଣନ୍ତୁ"</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 3e7ca92681c5..37a995a5a799 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -754,7 +754,7 @@ <string name="phoneTypeFaxHome" msgid="6678559953115904345">"Faks shtëpie"</string> <string name="phoneTypePager" msgid="576402072263522767">"Biper"</string> <string name="phoneTypeOther" msgid="6918196243648754715">"Tjetër"</string> - <string name="phoneTypeCallback" msgid="3455781500844157767">"Ri-telefono"</string> + <string name="phoneTypeCallback" msgid="3455781500844157767">"Kthim telefonate"</string> <string name="phoneTypeCar" msgid="4604775148963129195">"Numri i telefonit të makinës"</string> <string name="phoneTypeCompanyMain" msgid="4482773154536455441">"Numri kryesor i telefonit të kompanisë"</string> <string name="phoneTypeIsdn" msgid="2496238954533998512">"ISDN"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 854427b983a0..6b9caf15a745 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -983,9 +983,9 @@ <string name="menu_space_shortcut_label" msgid="5949311515646872071">"space"</string> <string name="menu_enter_shortcut_label" msgid="6709499510082897320">"enter"</string> <string name="menu_delete_shortcut_label" msgid="4365787714477739080">"delete"</string> - <string name="search_go" msgid="2141477624421347086">"వెతుకు"</string> + <string name="search_go" msgid="2141477624421347086">"సెర్చ్"</string> <string name="search_hint" msgid="455364685740251925">"వెతుకు..."</string> - <string name="searchview_description_search" msgid="1045552007537359343">"శోధించండి"</string> + <string name="searchview_description_search" msgid="1045552007537359343">"సెర్చ్"</string> <string name="searchview_description_query" msgid="7430242366971716338">"ప్రశ్నను శోధించండి"</string> <string name="searchview_description_clear" msgid="1989371719192982900">"ప్రశ్నను క్లియర్ చేయి"</string> <string name="searchview_description_submit" msgid="6771060386117334686">"ప్రశ్నని సమర్పించండి"</string> @@ -1401,7 +1401,7 @@ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"జూమ్ నియంత్రణ కోసం రెండుసార్లు నొక్కండి"</string> <string name="gadget_host_error_inflating" msgid="2449961590495198720">"విడ్జెట్ను జోడించడం సాధ్యపడలేదు."</string> <string name="ime_action_go" msgid="5536744546326495436">"వెళ్లు"</string> - <string name="ime_action_search" msgid="4501435960587287668">"వెతుకు"</string> + <string name="ime_action_search" msgid="4501435960587287668">"సెర్చ్"</string> <string name="ime_action_send" msgid="8456843745664334138">"పంపు"</string> <string name="ime_action_next" msgid="4169702997635728543">"తర్వాత"</string> <string name="ime_action_done" msgid="6299921014822891569">"పూర్తయింది"</string> @@ -1881,7 +1881,7 @@ <string name="language_picker_section_suggested" msgid="6556199184638990447">"సూచించినవి"</string> <string name="language_picker_section_all" msgid="1985809075777564284">"అన్ని భాషలు"</string> <string name="region_picker_section_all" msgid="756441309928774155">"అన్ని ప్రాంతాలు"</string> - <string name="locale_search_menu" msgid="6258090710176422934">"వెతుకు"</string> + <string name="locale_search_menu" msgid="6258090710176422934">"సెర్చ్"</string> <string name="app_suspended_title" msgid="888873445010322650">"యాప్ అందుబాటులో లేదు"</string> <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ప్రస్తుతం అందుబాటులో లేదు. ఇది <xliff:g id="APP_NAME_1">%2$s</xliff:g> ద్వారా నిర్వహించబడుతుంది."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"మరింత తెలుసుకోండి"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 3cb264da95c3..1899f8fd2077 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -1131,10 +1131,10 @@ <string name="whichViewApplication" msgid="5733194231473132945">"Mở bằng"</string> <string name="whichViewApplicationNamed" msgid="415164730629690105">"Mở bằng %1$s"</string> <string name="whichViewApplicationLabel" msgid="7367556735684742409">"Mở"</string> - <string name="whichOpenHostLinksWith" msgid="7645631470199397485">"Mở đường dẫn liên kết <xliff:g id="HOST">%1$s</xliff:g> bằng"</string> - <string name="whichOpenLinksWith" msgid="1120936181362907258">"Mở đường dẫn liên kết bằng"</string> - <string name="whichOpenLinksWithApp" msgid="6917864367861910086">"Mở đường dẫn liên kết bằng <xliff:g id="APPLICATION">%1$s</xliff:g>"</string> - <string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"Mở đường dẫn liên kết <xliff:g id="HOST">%1$s</xliff:g> bằng <xliff:g id="APPLICATION">%2$s</xliff:g>"</string> + <string name="whichOpenHostLinksWith" msgid="7645631470199397485">"Mở đường liên kết <xliff:g id="HOST">%1$s</xliff:g> bằng"</string> + <string name="whichOpenLinksWith" msgid="1120936181362907258">"Mở đường liên kết bằng"</string> + <string name="whichOpenLinksWithApp" msgid="6917864367861910086">"Mở đường liên kết bằng <xliff:g id="APPLICATION">%1$s</xliff:g>"</string> + <string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"Mở đường liên kết <xliff:g id="HOST">%1$s</xliff:g> bằng <xliff:g id="APPLICATION">%2$s</xliff:g>"</string> <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Cấp quyền truy cập"</string> <string name="whichEditApplication" msgid="6191568491456092812">"Chỉnh sửa bằng"</string> <string name="whichEditApplicationNamed" msgid="8096494987978521514">"Chỉnh sửa bằng %1$s"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 8913e8b18750..5f2e4f905b1c 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1823,6 +1823,8 @@ <string name="config_defaultCallScreening" translatable="false"></string> <!-- The name of the package that will hold the system gallery role. --> <string name="config_systemGallery" translatable="false">com.android.gallery3d</string> + <!-- The name of the package that will hold the system cluster service role. --> + <string name="config_systemAutomotiveCluster" translatable="false"></string> <!-- The name of the package that will be allowed to change its components' label/icon. --> <string name="config_overrideComponentUiPackage" translatable="false"></string> @@ -4187,6 +4189,8 @@ <string-array name="config_biometric_sensors" translatable="false" > <!-- <item>0:2:15</item> ID0:Fingerprint:Strong --> </string-array> + <!--If true, allows the device to load udfps components on older HIDL implementations --> + <bool name="allow_test_udfps" translatable="false" >false</bool> <!-- Messages that should not be shown to the user during face auth enrollment. This should be used to hide messages that may be too chatty or messages that the user can't do much about. @@ -4208,15 +4212,20 @@ </integer-array> <!-- Messages that should not be shown to the user during face authentication, on - BiometricPrompt. This should be used to hide messages that may be too chatty or messages that - the user can't do much about. Entries are defined in - android.hardware.biometrics.face@1.0 types.hal --> + BiometricPrompt. This should be used to hide messages that may be too chatty or messages + that the user can't do much about. Entries are defined in + android.hardware.biometrics.face@1.0 types.hal --> <integer-array name="config_face_acquire_biometricprompt_ignorelist" translatable="false" > </integer-array> <!-- Same as the above, but are defined by vendorCodes --> <integer-array name="config_face_acquire_vendor_biometricprompt_ignorelist" translatable="false" > </integer-array> + <!-- True if the sensor is able to provide self illumination in dark secnarios, without support + from above the HAL. This configuration is only applicable to IBiometricsFace@1.0 and its + minor revisions. --> + <bool name="config_faceAuthSupportsSelfIllumination">true</bool> + <!-- If face auth sends the user directly to home/last open app, or stays on keyguard --> <bool name="config_faceAuthDismissesKeyguard">true</bool> diff --git a/core/res/res/values/cross_profile_apps.xml b/core/res/res/values/cross_profile_apps.xml index ab6f20db0694..3688c3eb940b 100644 --- a/core/res/res/values/cross_profile_apps.xml +++ b/core/res/res/values/cross_profile_apps.xml @@ -17,7 +17,7 @@ <resources> <!-- A collection of apps that have been pre-approved for cross-profile communication. - These will not require admin consent, but will still require user consent during provisioning. + These will not require admin or user consent. --> <string-array translatable="false" name="cross_profile_apps"> </string-array> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 303fde6705c2..1e2d554a088b 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3069,7 +3069,8 @@ </public-group> <public-group type="string" first-id="0x01040028"> - <!-- string definitions go here --> + <!-- @hide @SystemApi @TestApi --> + <public name="config_systemAutomotiveCluster" /> </public-group> <public-group type="id" first-id="0x01020055"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 040fad5e6410..35ce780d3408 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2506,6 +2506,7 @@ <java-symbol type="string" name="face_error_security_update_required" /> <java-symbol type="array" name="config_biometric_sensors" /> + <java-symbol type="bool" name="allow_test_udfps" /> <java-symbol type="array" name="config_face_acquire_enroll_ignorelist" /> <java-symbol type="array" name="config_face_acquire_vendor_enroll_ignorelist" /> @@ -2513,6 +2514,7 @@ <java-symbol type="array" name="config_face_acquire_vendor_keyguard_ignorelist" /> <java-symbol type="array" name="config_face_acquire_biometricprompt_ignorelist" /> <java-symbol type="array" name="config_face_acquire_vendor_biometricprompt_ignorelist" /> + <java-symbol type="bool" name="config_faceAuthSupportsSelfIllumination" /> <java-symbol type="bool" name="config_faceAuthDismissesKeyguard" /> <!-- Face config --> diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml index d8ec72f33ddc..166edca3d046 100644 --- a/core/res/res/xml/power_profile.xml +++ b/core/res/res/xml/power_profile.xml @@ -20,7 +20,7 @@ <device name="Android"> <!-- Most values are the incremental current used by a feature, in mA (measured at nominal voltage). - The default values are deliberately incorrect dummy values. + The default values are deliberately incorrect values. OEM's must measure and provide actual values before shipping a device. Example real-world values are given in comments, but they diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index e0d702e98595..2bf9848304ad 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -24,6 +24,7 @@ import static android.app.servertransaction.TestUtils.resultInfoList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.app.ContentProviderHolder; import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; import android.app.IUiAutomationConnection; @@ -665,5 +666,10 @@ public class TransactionParcelTests { public void performDirectAction(IBinder activityToken, String actionId, Bundle arguments, RemoteCallback cancellationCallback, RemoteCallback resultCallback) { } + + @Override + public void notifyContentProviderPublishStatus(ContentProviderHolder holder, String auth, + int userId, boolean published) { + } } } diff --git a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java index 8a36f1dc057b..72391f4d7dec 100644 --- a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java +++ b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java @@ -33,9 +33,11 @@ public class TimeZoneCapabilitiesTest { public void testEquals() { TimeZoneCapabilities.Builder builder1 = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID) .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED) + .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED) .setSuggestManualTimeZone(CAPABILITY_POSSESSED); TimeZoneCapabilities.Builder builder2 = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID) .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED) + .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED) .setSuggestManualTimeZone(CAPABILITY_POSSESSED); { TimeZoneCapabilities one = builder1.build(); @@ -57,6 +59,20 @@ public class TimeZoneCapabilitiesTest { assertEquals(one, two); } + builder2.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED); + { + TimeZoneCapabilities one = builder1.build(); + TimeZoneCapabilities two = builder2.build(); + assertNotEquals(one, two); + } + + builder1.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED); + { + TimeZoneCapabilities one = builder1.build(); + TimeZoneCapabilities two = builder2.build(); + assertEquals(one, two); + } + builder2.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED); { TimeZoneCapabilities one = builder1.build(); @@ -76,12 +92,16 @@ public class TimeZoneCapabilitiesTest { public void testParcelable() { TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID) .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED) + .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED) .setSuggestManualTimeZone(CAPABILITY_POSSESSED); assertRoundTripParcelable(builder.build()); builder.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED); assertRoundTripParcelable(builder.build()); + builder.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED); + assertRoundTripParcelable(builder.build()); + builder.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED); assertRoundTripParcelable(builder.build()); } diff --git a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java index ac7e9c437b63..00dc73ed269f 100644 --- a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java +++ b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java @@ -29,8 +29,9 @@ public class TimeZoneConfigurationTest { @Test public void testBuilder_copyConstructor() { - TimeZoneConfiguration.Builder builder1 = - new TimeZoneConfiguration.Builder().setAutoDetectionEnabled(true); + TimeZoneConfiguration.Builder builder1 = new TimeZoneConfiguration.Builder() + .setAutoDetectionEnabled(true) + .setGeoDetectionEnabled(true); TimeZoneConfiguration configuration1 = builder1.build(); TimeZoneConfiguration configuration2 = @@ -41,16 +42,15 @@ public class TimeZoneConfigurationTest { @Test public void testIsComplete() { - TimeZoneConfiguration incompleteConfiguration = - new TimeZoneConfiguration.Builder() - .build(); - assertFalse(incompleteConfiguration.isComplete()); + TimeZoneConfiguration.Builder builder = + new TimeZoneConfiguration.Builder(); + assertFalse(builder.build().isComplete()); - TimeZoneConfiguration completeConfiguration = - new TimeZoneConfiguration.Builder() - .setAutoDetectionEnabled(true) - .build(); - assertTrue(completeConfiguration.isComplete()); + builder.setAutoDetectionEnabled(true); + assertFalse(builder.build().isComplete()); + + builder.setGeoDetectionEnabled(true); + assertTrue(builder.build().isComplete()); } @Test @@ -122,6 +122,27 @@ public class TimeZoneConfigurationTest { TimeZoneConfiguration two = builder2.build(); assertEquals(one, two); } + + builder1.setGeoDetectionEnabled(true); + { + TimeZoneConfiguration one = builder1.build(); + TimeZoneConfiguration two = builder2.build(); + assertNotEquals(one, two); + } + + builder2.setGeoDetectionEnabled(false); + { + TimeZoneConfiguration one = builder1.build(); + TimeZoneConfiguration two = builder2.build(); + assertNotEquals(one, two); + } + + builder1.setGeoDetectionEnabled(false); + { + TimeZoneConfiguration one = builder1.build(); + TimeZoneConfiguration two = builder2.build(); + assertEquals(one, two); + } } @Test @@ -135,5 +156,11 @@ public class TimeZoneConfigurationTest { builder.setAutoDetectionEnabled(false); assertRoundTripParcelable(builder.build()); + + builder.setGeoDetectionEnabled(false); + assertRoundTripParcelable(builder.build()); + + builder.setGeoDetectionEnabled(true); + assertRoundTripParcelable(builder.build()); } } diff --git a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java index 01515bd9c6b5..a80f5a03ee4e 100644 --- a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java @@ -52,7 +52,9 @@ import android.os.SystemClock; import android.provider.Settings; import android.support.test.uiautomator.UiDevice; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.DebugUtils; +import android.util.KeyValueListParser; import android.util.Log; import androidx.test.InstrumentationRegistry; @@ -104,8 +106,6 @@ public class BstatsCpuTimesValidationTest { private static final int WORK_DURATION_MS = 2000; - private static final String DESIRED_PROC_STATE_CPU_TIMES_DELAY = "0"; - private static boolean sBatteryStatsConstsUpdated; private static String sOriginalBatteryStatsConsts; @@ -124,10 +124,12 @@ public class BstatsCpuTimesValidationTest { sContext.getPackageManager().setApplicationEnabledSetting(TEST_PKG, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0); sTestPkgUid = sContext.getPackageManager().getPackageUid(TEST_PKG, 0); + + final ArrayMap<String, String> desiredConstants = new ArrayMap<>(); + desiredConstants.put(KEY_TRACK_CPU_TIMES_BY_PROC_STATE, Boolean.toString(true)); + desiredConstants.put(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS, Integer.toString(0)); + updateBatteryStatsConstants(desiredConstants); checkCpuTimesAvailability(); - if (sPerProcStateTimesAvailable && sCpuFreqTimesAvailable) { - setDesiredReadyDelay(); - } } @AfterClass @@ -139,26 +141,33 @@ public class BstatsCpuTimesValidationTest { batteryReset(); } - private static void setDesiredReadyDelay() { + private static void updateBatteryStatsConstants(ArrayMap<String, String> desiredConstants) { sOriginalBatteryStatsConsts = Settings.Global.getString(sContext.getContentResolver(), Settings.Global.BATTERY_STATS_CONSTANTS); - String newBatteryStatsConstants; - final String newConstant = KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS - + "=" + DESIRED_PROC_STATE_CPU_TIMES_DELAY; - if (sOriginalBatteryStatsConsts == null || "null".equals(sOriginalBatteryStatsConsts)) { - // battery_stats_constants is initially empty, so just assign the desired value. - newBatteryStatsConstants = newConstant; - } else if (sOriginalBatteryStatsConsts.contains(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS)) { - // battery_stats_constants contains delay duration, so replace it - // with the desired value. - newBatteryStatsConstants = sOriginalBatteryStatsConsts.replaceAll( - KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS + "=\\d+", newConstant); - } else { - // battery_stats_constants didn't contain any delay, so append the desired value. - newBatteryStatsConstants = sOriginalBatteryStatsConsts + "," + newConstant; + final char delimiter = ','; + final KeyValueListParser parser = new KeyValueListParser(delimiter); + parser.setString(sOriginalBatteryStatsConsts); + final StringBuilder sb = new StringBuilder(); + for (int i = 0, size = parser.size(); i < size; ++i) { + final String key = parser.keyAt(i); + final String value = desiredConstants.getOrDefault(key, + parser.getString(key, null)); + if (sb.length() > 0) { + sb.append(delimiter); + } + sb.append(key + "=" + value); + desiredConstants.remove(key); } + desiredConstants.forEach((key, value) -> { + if (sb.length() > 0) { + sb.append(delimiter); + } + sb.append(key + '=' + value); + }); Settings.Global.putString(sContext.getContentResolver(), - Settings.Global.BATTERY_STATS_CONSTANTS, newBatteryStatsConstants); + Settings.Global.BATTERY_STATS_CONSTANTS, sb.toString()); + Log.d(TAG, "Updated value of '" + Settings.Global.BATTERY_STATS_CONSTANTS + "': " + + sb.toString()); sBatteryStatsConstsUpdated = true; } diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp index 04007c91817d..d0e688d3efc1 100644 --- a/data/etc/car/Android.bp +++ b/data/etc/car/Android.bp @@ -17,126 +17,126 @@ // Privapp permission whitelist files prebuilt_etc { - name: "privapp_whitelist_android.car.cluster.loggingrenderer", + name: "allowed_privapp_android.car.cluster.loggingrenderer", sub_dir: "permissions", src: "android.car.cluster.loggingrenderer.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_android.car.cluster.sample", + name: "allowed_privapp_android.car.cluster.sample", sub_dir: "permissions", src: "android.car.cluster.sample.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_android.car.cluster", + name: "allowed_privapp_android.car.cluster", sub_dir: "permissions", src: "android.car.cluster.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_android.car.usb.handler", + name: "allowed_privapp_android.car.usb.handler", sub_dir: "permissions", src: "android.car.usb.handler.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_com.android.car.carlauncher", + name: "allowed_privapp_com.android.car.carlauncher", sub_dir: "permissions", src: "com.android.car.carlauncher.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_com.android.car.dialer", + name: "allowed_privapp_com.android.car.dialer", sub_dir: "permissions", src: "com.android.car.dialer.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_com.android.car.hvac", + name: "allowed_privapp_com.android.car.hvac", sub_dir: "permissions", src: "com.android.car.hvac.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_com.android.car.media", + name: "allowed_privapp_com.android.car.media", sub_dir: "permissions", src: "com.android.car.media.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_com.android.car.notification", + name: "allowed_privapp_com.android.car.notification", sub_dir: "permissions", src: "com.android.car.notification.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_com.android.car.radio", + name: "allowed_privapp_com.android.car.radio", sub_dir: "permissions", src: "com.android.car.radio.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_com.android.car.secondaryhome", + name: "allowed_privapp_com.android.car.secondaryhome", sub_dir: "permissions", src: "com.android.car.secondaryhome.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_com.android.car.settings", + name: "allowed_privapp_com.android.car.settings", sub_dir: "permissions", src: "com.android.car.settings.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_com.android.car.themeplayground", + name: "allowed_privapp_com.android.car.themeplayground", sub_dir: "permissions", src: "com.android.car.themeplayground.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_com.android.car", + name: "allowed_privapp_com.android.car", sub_dir: "permissions", src: "com.android.car.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_com.android.car.bugreport", + name: "allowed_privapp_com.android.car.bugreport", sub_dir: "permissions", src: "com.android.car.bugreport.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_com.android.car.companiondevicesupport", + name: "allowed_privapp_com.android.car.companiondevicesupport", sub_dir: "permissions", src: "com.android.car.companiondevicesupport.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_com.google.android.car.kitchensink", + name: "allowed_privapp_com.google.android.car.kitchensink", sub_dir: "permissions", src: "com.google.android.car.kitchensink.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_com.android.car.developeroptions", + name: "allowed_privapp_com.android.car.developeroptions", sub_dir: "permissions", src: "com.android.car.developeroptions.xml", filename_from_src: true, @@ -144,14 +144,14 @@ prebuilt_etc { } prebuilt_etc { - name: "privapp_whitelist_com.android.car.floatingcardslauncher", + name: "allowed_privapp_com.android.car.floatingcardslauncher", sub_dir: "permissions", src: "com.android.car.floatingcardslauncher.xml", filename_from_src: true, } prebuilt_etc { - name: "privapp_whitelist_com.android.car.ui.paintbooth", + name: "allowed_privapp_com.android.car.ui.paintbooth", sub_dir: "permissions", src: "com.android.car.ui.paintbooth.xml", filename_from_src: true, diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml index ada8b000a26b..06f1dae30cd3 100644 --- a/data/etc/com.android.systemui.xml +++ b/data/etc/com.android.systemui.xml @@ -35,6 +35,7 @@ <permission name="android.permission.MANAGE_USERS"/> <permission name="android.permission.MASTER_CLEAR"/> <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> + <permission name="android.permission.MODIFY_AUDIO_ROUTING" /> <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/> <permission name="android.permission.MODIFY_PHONE_STATE"/> <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java index ba3ebddd4b86..4ec752a6dd9e 100644 --- a/drm/java/android/drm/DrmManagerClient.java +++ b/drm/java/android/drm/DrmManagerClient.java @@ -751,7 +751,7 @@ public class DrmManagerClient implements AutoCloseable { /** * Removes all the rights information of every DRM plug-in (agent) associated with - * the DRM framework. Will be used during a master reset. + * the DRM framework. * * @return ERROR_NONE for success; ERROR_UNKNOWN for failure. */ diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 28d7911c771f..964640b106b9 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -3111,7 +3111,7 @@ public class Paint { @CriticalNative private static native boolean nGetFillPath(long paintPtr, long src, long dst); @CriticalNative - private static native long nSetShader(long paintPtr, long shader); + private static native void nSetShader(long paintPtr, long shader); @CriticalNative private static native long nSetColorFilter(long paintPtr, long filter); @CriticalNative diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp index e713b98b867e..9d83e491fdc1 100644 --- a/libs/hostgraphics/Android.bp +++ b/libs/hostgraphics/Android.bp @@ -5,6 +5,10 @@ cc_library_host_static { "-Wno-unused-parameter", ], + static_libs: [ + "libbase", + ], + srcs: [ ":libui_host_common", "Fence.cpp", @@ -28,4 +32,4 @@ cc_library_host_static { enabled: true, } }, -}
\ No newline at end of file +} diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 9ae5ad97ed36..0b13754271b9 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -462,6 +462,13 @@ cc_defaults { "RenderNode.cpp", "RenderProperties.cpp", "RootRenderNode.cpp", + "shader/Shader.cpp", + "shader/BitmapShader.cpp", + "shader/ComposeShader.cpp", + "shader/LinearGradientShader.cpp", + "shader/RadialGradientShader.cpp", + "shader/RuntimeShader.cpp", + "shader/SweepGradientShader.cpp", "SkiaCanvas.cpp", "VectorDrawable.cpp", ], diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 242b8b0d139e..cfba5d4f6aa2 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -42,6 +42,8 @@ #include <SkTextBlob.h> #include <SkVertices.h> +#include <shader/BitmapShader.h> + #include <memory> #include <optional> #include <utility> @@ -49,6 +51,7 @@ namespace android { using uirenderer::PaintUtils; +using uirenderer::BitmapShader; Canvas* Canvas::create_canvas(const SkBitmap& bitmap) { return new SkiaCanvas(bitmap); @@ -681,7 +684,9 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, if (paint) { pnt = *paint; } - pnt.setShader(bitmap.makeImage()->makeShader()); + + pnt.setShader(sk_ref_sp(new BitmapShader( + bitmap.makeImage(), SkTileMode::kClamp, SkTileMode::kClamp, nullptr))); auto v = builder.detach(); apply_looper(&pnt, [&](const SkPaint& p) { mCanvas->drawVertices(v, SkBlendMode::kModulate, p); diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index cd908354aea5..0f566e4b494a 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -23,7 +23,6 @@ #include "PathParser.h" #include "SkColorFilter.h" #include "SkImageInfo.h" -#include "SkShader.h" #include "hwui/Paint.h" #ifdef __ANDROID__ @@ -159,10 +158,10 @@ void FullPath::draw(SkCanvas* outCanvas, bool useStagingData) { // Draw path's fill, if fill color or gradient is valid bool needsFill = false; - SkPaint paint; + Paint paint; if (properties.getFillGradient() != nullptr) { paint.setColor(applyAlpha(SK_ColorBLACK, properties.getFillAlpha())); - paint.setShader(sk_sp<SkShader>(SkSafeRef(properties.getFillGradient()))); + paint.setShader(sk_sp<Shader>(SkSafeRef(properties.getFillGradient()))); needsFill = true; } else if (properties.getFillColor() != SK_ColorTRANSPARENT) { paint.setColor(applyAlpha(properties.getFillColor(), properties.getFillAlpha())); @@ -179,7 +178,7 @@ void FullPath::draw(SkCanvas* outCanvas, bool useStagingData) { bool needsStroke = false; if (properties.getStrokeGradient() != nullptr) { paint.setColor(applyAlpha(SK_ColorBLACK, properties.getStrokeAlpha())); - paint.setShader(sk_sp<SkShader>(SkSafeRef(properties.getStrokeGradient()))); + paint.setShader(sk_sp<Shader>(SkSafeRef(properties.getStrokeGradient()))); needsStroke = true; } else if (properties.getStrokeColor() != SK_ColorTRANSPARENT) { paint.setColor(applyAlpha(properties.getStrokeColor(), properties.getStrokeAlpha())); diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index ac7d41e0d600..d4086f1aa622 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -31,8 +31,8 @@ #include <SkPath.h> #include <SkPathMeasure.h> #include <SkRect.h> -#include <SkShader.h> #include <SkSurface.h> +#include <shader/Shader.h> #include <cutils/compiler.h> #include <stddef.h> @@ -227,20 +227,20 @@ public: strokeGradient = prop.strokeGradient; onPropertyChanged(); } - void setFillGradient(SkShader* gradient) { + void setFillGradient(Shader* gradient) { if (fillGradient.get() != gradient) { fillGradient = sk_ref_sp(gradient); onPropertyChanged(); } } - void setStrokeGradient(SkShader* gradient) { + void setStrokeGradient(Shader* gradient) { if (strokeGradient.get() != gradient) { strokeGradient = sk_ref_sp(gradient); onPropertyChanged(); } } - SkShader* getFillGradient() const { return fillGradient.get(); } - SkShader* getStrokeGradient() const { return strokeGradient.get(); } + Shader* getFillGradient() const { return fillGradient.get(); } + Shader* getStrokeGradient() const { return strokeGradient.get(); } float getStrokeWidth() const { return mPrimitiveFields.strokeWidth; } void setStrokeWidth(float strokeWidth) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeWidth, strokeWidth); @@ -320,8 +320,8 @@ public: count, }; PrimitiveFields mPrimitiveFields; - sk_sp<SkShader> fillGradient; - sk_sp<SkShader> strokeGradient; + sk_sp<Shader> fillGradient; + sk_sp<Shader> strokeGradient; }; // Called from UI thread diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index c138a32eacc2..2a377bbb83f2 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -73,7 +73,7 @@ void Canvas::drawTextDecorations(float x, float y, float length, const Paint& pa static void simplifyPaint(int color, Paint* paint) { paint->setColor(color); - paint->setShader(nullptr); + paint->setShader((sk_sp<uirenderer::Shader>)nullptr); paint->setColorFilter(nullptr); paint->setLooper(nullptr); paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize()); diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h index e75e9e7c6933..0bb689c19079 100644 --- a/libs/hwui/hwui/Paint.h +++ b/libs/hwui/hwui/Paint.h @@ -30,6 +30,8 @@ #include <minikin/FamilyVariant.h> #include <minikin/Hyphenator.h> +#include <shader/Shader.h> + namespace android { class Paint : public SkPaint { @@ -149,8 +151,14 @@ public: // The only respected flags are : [ antialias, dither, filterBitmap ] static uint32_t GetSkPaintJavaFlags(const SkPaint&); static void SetSkPaintJavaFlags(SkPaint*, uint32_t flags); + + void setShader(sk_sp<uirenderer::Shader> shader); private: + + using SkPaint::setShader; + using SkPaint::setImageFilter; + SkFont mFont; sk_sp<SkDrawLooper> mLooper; @@ -169,6 +177,7 @@ private: bool mStrikeThru = false; bool mUnderline = false; bool mDevKern = false; + sk_sp<uirenderer::Shader> mShader; }; } // namespace android diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp index fa2674fc2f5e..21f60fd7b671 100644 --- a/libs/hwui/hwui/PaintImpl.cpp +++ b/libs/hwui/hwui/PaintImpl.cpp @@ -24,7 +24,8 @@ Paint::Paint() , mWordSpacing(0) , mFontFeatureSettings() , mMinikinLocaleListId(0) - , mFamilyVariant(minikin::FamilyVariant::DEFAULT) { + , mFamilyVariant(minikin::FamilyVariant::DEFAULT) + , mShader(nullptr) { // SkPaint::antialiasing defaults to false, but // SkFont::edging defaults to kAntiAlias. To keep them // insync, we manually set the font to kAilas. @@ -45,7 +46,8 @@ Paint::Paint(const Paint& paint) , mAlign(paint.mAlign) , mStrikeThru(paint.mStrikeThru) , mUnderline(paint.mUnderline) - , mDevKern(paint.mDevKern) {} + , mDevKern(paint.mDevKern) + , mShader(paint.mShader){} Paint::~Paint() {} @@ -65,9 +67,30 @@ Paint& Paint::operator=(const Paint& other) { mStrikeThru = other.mStrikeThru; mUnderline = other.mUnderline; mDevKern = other.mDevKern; + mShader = other.mShader; return *this; } +void Paint::setShader(sk_sp<uirenderer::Shader> shader) { + if (shader) { + // If there is an SkShader compatible shader, apply it + sk_sp<SkShader> skShader = shader->asSkShader(); + if (skShader.get()) { + SkPaint::setShader(skShader); + SkPaint::setImageFilter(nullptr); + } else { + // ... otherwise the specified shader can only be represented as an ImageFilter + SkPaint::setShader(nullptr); + SkPaint::setImageFilter(shader->asSkImageFilter()); + } + } else { + // No shader is provided at all, clear out both the SkShader and SkImageFilter slots + SkPaint::setShader(nullptr); + SkPaint::setImageFilter(nullptr); + } + mShader = shader; +} + bool operator==(const Paint& a, const Paint& b) { return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) && a.mFont == b.mFont && diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp index df8635a8fe5a..554674a331cd 100644 --- a/libs/hwui/jni/Paint.cpp +++ b/libs/hwui/jni/Paint.cpp @@ -47,6 +47,7 @@ #include <minikin/LocaleList.h> #include <minikin/Measurement.h> #include <minikin/MinikinPaint.h> +#include <shader/Shader.h> #include <unicode/utf16.h> #include <cassert> @@ -54,6 +55,8 @@ #include <memory> #include <vector> +using namespace android::uirenderer; + namespace android { struct JMetricsID { @@ -782,11 +785,10 @@ namespace PaintGlue { return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE; } - static jlong setShader(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong shaderHandle) { - Paint* obj = reinterpret_cast<Paint*>(objHandle); - SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle); - obj->setShader(sk_ref_sp(shader)); - return reinterpret_cast<jlong>(obj->getShader()); + static void setShader(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong shaderHandle) { + auto* paint = reinterpret_cast<Paint*>(objHandle); + auto* shader = reinterpret_cast<Shader*>(shaderHandle); + paint->setShader(sk_ref_sp(shader)); } static jlong setColorFilter(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong filterHandle) { @@ -1097,7 +1099,7 @@ static const JNINativeMethod methods[] = { {"nGetStrokeJoin","(J)I", (void*) PaintGlue::getStrokeJoin}, {"nSetStrokeJoin","(JI)V", (void*) PaintGlue::setStrokeJoin}, {"nGetFillPath","(JJJ)Z", (void*) PaintGlue::getFillPath}, - {"nSetShader","(JJ)J", (void*) PaintGlue::setShader}, + {"nSetShader","(JJ)V", (void*) PaintGlue::setShader}, {"nSetColorFilter","(JJ)J", (void*) PaintGlue::setColorFilter}, {"nSetXfermode","(JI)V", (void*) PaintGlue::setXfermode}, {"nSetPathEffect","(JJ)J", (void*) PaintGlue::setPathEffect}, diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index e76aace601be..9b1972ed664f 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -5,6 +5,13 @@ #include "SkShader.h" #include "SkBlendMode.h" #include "include/effects/SkRuntimeEffect.h" +#include "shader/Shader.h" +#include "shader/BitmapShader.h" +#include "shader/ComposeShader.h" +#include "shader/LinearGradientShader.h" +#include "shader/RadialGradientShader.h" +#include "shader/RuntimeShader.h" +#include "shader/SweepGradientShader.h" #include <vector> @@ -50,7 +57,7 @@ static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvAr /////////////////////////////////////////////////////////////////////////////////////////////// -static void Shader_safeUnref(SkShader* shader) { +static void Shader_safeUnref(Shader* shader) { SkSafeUnref(shader); } @@ -74,15 +81,15 @@ static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, j SkBitmap bitmap; image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); } - sk_sp<SkShader> shader = image->makeShader( - (SkTileMode)tileModeX, (SkTileMode)tileModeY); - ThrowIAE_IfNull(env, shader.get()); - if (matrix) { - shader = shader->makeWithLocalMatrix(*matrix); - } + auto* shader = new BitmapShader( + image, + static_cast<SkTileMode>(tileModeX), + static_cast<SkTileMode>(tileModeY), + matrix + ); - return reinterpret_cast<jlong>(shader.release()); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -118,17 +125,18 @@ static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr, #error Need to convert float array to SkScalar array before calling the following function. #endif - sk_sp<SkShader> shader(SkGradientShader::MakeLinear(pts, &colors[0], - GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), - static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr)); - ThrowIAE_IfNull(env, shader); - - const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - if (matrix) { - shader = shader->makeWithLocalMatrix(*matrix); - } + auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); + auto* shader = new LinearGradientShader( + pts, + colors, + GraphicsJNI::getNativeColorSpace(colorSpaceHandle), + pos, + static_cast<SkTileMode>(tileMode), + sGradientShaderFlags, + matrix + ); - return reinterpret_cast<jlong>(shader.release()); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -148,17 +156,20 @@ static jlong RadialGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat #error Need to convert float array to SkScalar array before calling the following function. #endif - sk_sp<SkShader> shader = SkGradientShader::MakeRadial(center, radius, &colors[0], - GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), - static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr); - ThrowIAE_IfNull(env, shader); + auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - if (matrix) { - shader = shader->makeWithLocalMatrix(*matrix); - } + auto* shader = new RadialGradientShader( + center, + radius, + colors, + GraphicsJNI::getNativeColorSpace(colorSpaceHandle), + pos, + static_cast<SkTileMode>(tileMode), + sGradientShaderFlags, + matrix + ); - return reinterpret_cast<jlong>(shader.release()); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////// @@ -174,54 +185,58 @@ static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat #error Need to convert float array to SkScalar array before calling the following function. #endif - sk_sp<SkShader> shader = SkGradientShader::MakeSweep(x, y, &colors[0], - GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), - sGradientShaderFlags, nullptr); - ThrowIAE_IfNull(env, shader); + auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - if (matrix) { - shader = shader->makeWithLocalMatrix(*matrix); - } + auto* shader = new SweepGradientShader( + x, + y, + colors, + GraphicsJNI::getNativeColorSpace(colorSpaceHandle), + pos, + sGradientShaderFlags, + matrix + ); - return reinterpret_cast<jlong>(shader.release()); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////////////////////// static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr, jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle) { - const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle); - SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle); - SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle); - sk_sp<SkShader> baseShader(SkShaders::Blend(mode, - sk_ref_sp(shaderA), sk_ref_sp(shaderB))); - - SkShader* shader; - - if (matrix) { - shader = baseShader->makeWithLocalMatrix(*matrix).release(); - } else { - shader = baseShader.release(); - } - return reinterpret_cast<jlong>(shader); + auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); + auto* shaderA = reinterpret_cast<Shader*>(shaderAHandle); + auto* shaderB = reinterpret_cast<Shader*>(shaderBHandle); + + auto mode = static_cast<SkBlendMode>(xfermodeHandle); + + auto* composeShader = new ComposeShader( + *shaderA, + *shaderB, + mode, + matrix + ); + + return reinterpret_cast<jlong>(composeShader); } /////////////////////////////////////////////////////////////////////////////////////////////// static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr, jbyteArray inputs, jlong colorSpaceHandle, jboolean isOpaque) { - SkRuntimeEffect* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory); + auto* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory); AutoJavaByteArray arInputs(env, inputs); - sk_sp<SkData> fData; - fData = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length()); - const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - sk_sp<SkShader> shader = effect->makeShader(fData, nullptr, 0, matrix, isOpaque == JNI_TRUE); - ThrowIAE_IfNull(env, shader); + auto data = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length()); + auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - return reinterpret_cast<jlong>(shader.release()); + auto* shader = new RuntimeShader( + *effect, + std::move(data), + isOpaque == JNI_TRUE, + matrix + ); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -239,12 +254,8 @@ static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sks /////////////////////////////////////////////////////////////////////////////////////////////// -static void Effect_safeUnref(SkRuntimeEffect* effect) { - SkSafeUnref(effect); -} - static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) { - return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Effect_safeUnref)); + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Shader_safeUnref)); } /////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp index 9cffceb308c8..a1adcb30e80d 100644 --- a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp +++ b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp @@ -143,13 +143,13 @@ static void updateFullPathPropertiesAndStrokeStyles(JNIEnv*, jobject, jlong full static void updateFullPathFillGradient(JNIEnv*, jobject, jlong pathPtr, jlong fillGradientPtr) { VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr); - SkShader* fillShader = reinterpret_cast<SkShader*>(fillGradientPtr); + auto* fillShader = reinterpret_cast<Shader*>(fillGradientPtr); path->mutateStagingProperties()->setFillGradient(fillShader); } static void updateFullPathStrokeGradient(JNIEnv*, jobject, jlong pathPtr, jlong strokeGradientPtr) { VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr); - SkShader* strokeShader = reinterpret_cast<SkShader*>(strokeGradientPtr); + auto* strokeShader = reinterpret_cast<Shader*>(strokeGradientPtr); path->mutateStagingProperties()->setStrokeGradient(strokeShader); } diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 2a8aa8c3e5b7..a11678189bad 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -238,12 +238,8 @@ EGLConfig EglManager::loadFP16Config(EGLDisplay display, SwapBehavior swapBehavi return config; } -extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); - void EglManager::initExtensions() { auto extensions = StringUtils::split(eglQueryString(mEglDisplay, EGL_EXTENSIONS)); - auto extensionsAndroid = - StringUtils::split(eglQueryStringImplementationANDROID(mEglDisplay, EGL_EXTENSIONS)); // For our purposes we don't care if EGL_BUFFER_AGE is a result of // EGL_EXT_buffer_age or EGL_KHR_partial_update as our usage is covered @@ -265,10 +261,7 @@ void EglManager::initExtensions() { EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context"); EglExtensions.fenceSync = extensions.has("EGL_KHR_fence_sync"); EglExtensions.waitSync = extensions.has("EGL_KHR_wait_sync"); - - // EGL_ANDROID_native_fence_sync is not exposed to applications, so access - // this through the private Android-specific query instead. - EglExtensions.nativeFenceSync = extensionsAndroid.has("EGL_ANDROID_native_fence_sync"); + EglExtensions.nativeFenceSync = extensions.has("EGL_ANDROID_native_fence_sync"); } bool EglManager::hasEglContext() { diff --git a/libs/hwui/shader/BitmapShader.cpp b/libs/hwui/shader/BitmapShader.cpp new file mode 100644 index 000000000000..fe653e85a021 --- /dev/null +++ b/libs/hwui/shader/BitmapShader.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "BitmapShader.h" + +#include "SkImagePriv.h" + +namespace android::uirenderer { +BitmapShader::BitmapShader(const sk_sp<SkImage>& image, const SkTileMode tileModeX, + const SkTileMode tileModeY, const SkMatrix* matrix) + : Shader(matrix), skShader(image->makeShader(tileModeX, tileModeY)) {} + +sk_sp<SkShader> BitmapShader::makeSkShader() { + return skShader; +} + +BitmapShader::~BitmapShader() {} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/BitmapShader.h b/libs/hwui/shader/BitmapShader.h new file mode 100644 index 000000000000..ed6a6e6802d1 --- /dev/null +++ b/libs/hwui/shader/BitmapShader.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" + +namespace android::uirenderer { + +/** + * Shader implementation that renders a Bitmap as either a SkShader or SkImageFilter + */ +class BitmapShader : public Shader { +public: + BitmapShader(const sk_sp<SkImage>& image, const SkTileMode tileModeX, + const SkTileMode tileModeY, const SkMatrix* matrix); + ~BitmapShader() override; + +protected: + sk_sp<SkShader> makeSkShader() override; + +private: + sk_sp<SkShader> skShader; +}; +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/ComposeShader.cpp b/libs/hwui/shader/ComposeShader.cpp new file mode 100644 index 000000000000..3765489e7431 --- /dev/null +++ b/libs/hwui/shader/ComposeShader.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "ComposeShader.h" + +#include "SkImageFilters.h" +#include "SkShader.h" + +namespace android::uirenderer { + +ComposeShader::ComposeShader(Shader& shaderA, Shader& shaderB, const SkBlendMode blendMode, + const SkMatrix* matrix) + : Shader(matrix) { + // If both Shaders can be represented as SkShaders then use those, if not + // create an SkImageFilter from both Shaders and create the equivalent SkImageFilter + sk_sp<SkShader> skShaderA = shaderA.asSkShader(); + sk_sp<SkShader> skShaderB = shaderB.asSkShader(); + if (skShaderA.get() && skShaderB.get()) { + skShader = SkShaders::Blend(blendMode, skShaderA, skShaderB); + skImageFilter = nullptr; + } else { + sk_sp<SkImageFilter> skImageFilterA = shaderA.asSkImageFilter(); + sk_sp<SkImageFilter> skImageFilterB = shaderB.asSkImageFilter(); + skShader = nullptr; + skImageFilter = SkImageFilters::Xfermode(blendMode, skImageFilterA, skImageFilterB); + } +} + +sk_sp<SkShader> ComposeShader::makeSkShader() { + return skShader; +} + +sk_sp<SkImageFilter> ComposeShader::makeSkImageFilter() { + return skImageFilter; +} + +ComposeShader::~ComposeShader() {} +} // namespace android::uirenderer diff --git a/libs/hwui/shader/ComposeShader.h b/libs/hwui/shader/ComposeShader.h new file mode 100644 index 000000000000..a246b520d46a --- /dev/null +++ b/libs/hwui/shader/ComposeShader.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" + +namespace android::uirenderer { + +/** + * Shader implementation that can composite 2 Shaders together with the specified blend mode. + * This implementation can appropriately convert the composed result to either an SkShader or + * SkImageFilter depending on the inputs + */ +class ComposeShader : public Shader { +public: + ComposeShader(Shader& shaderA, Shader& shaderB, const SkBlendMode blendMode, + const SkMatrix* matrix); + ~ComposeShader() override; + +protected: + sk_sp<SkShader> makeSkShader() override; + sk_sp<SkImageFilter> makeSkImageFilter() override; + +private: + sk_sp<SkShader> skShader; + sk_sp<SkImageFilter> skImageFilter; +}; +} // namespace android::uirenderer diff --git a/libs/hwui/shader/LinearGradientShader.cpp b/libs/hwui/shader/LinearGradientShader.cpp new file mode 100644 index 000000000000..868fa44fb4b7 --- /dev/null +++ b/libs/hwui/shader/LinearGradientShader.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "LinearGradientShader.h" + +#include <vector> + +#include "SkGradientShader.h" + +namespace android::uirenderer { + +LinearGradientShader::LinearGradientShader(const SkPoint pts[2], + const std::vector<SkColor4f>& colors, + sk_sp<SkColorSpace> colorspace, const SkScalar pos[], + const SkTileMode tileMode, const uint32_t shaderFlags, + const SkMatrix* matrix) + : Shader(matrix) + , skShader(SkGradientShader::MakeLinear(pts, colors.data(), colorspace, pos, colors.size(), + tileMode, shaderFlags, nullptr)) {} + +sk_sp<SkShader> LinearGradientShader::makeSkShader() { + return skShader; +} + +LinearGradientShader::~LinearGradientShader() {} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/LinearGradientShader.h b/libs/hwui/shader/LinearGradientShader.h new file mode 100644 index 000000000000..596f4e009448 --- /dev/null +++ b/libs/hwui/shader/LinearGradientShader.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" + +namespace android::uirenderer { + +/** + * Shader implementation that renders a color ramp of colors to either as either SkShader or + * SkImageFilter + */ +class LinearGradientShader : public Shader { +public: + LinearGradientShader(const SkPoint pts[2], const std::vector<SkColor4f>& colors, + sk_sp<SkColorSpace> colorspace, const SkScalar pos[], + const SkTileMode tileMode, const uint32_t shaderFlags, + const SkMatrix* matrix); + ~LinearGradientShader() override; + +protected: + sk_sp<SkShader> makeSkShader() override; + +private: + sk_sp<SkShader> skShader; +}; +} // namespace android::uirenderer diff --git a/libs/hwui/shader/RadialGradientShader.cpp b/libs/hwui/shader/RadialGradientShader.cpp new file mode 100644 index 000000000000..21ff56fee2f8 --- /dev/null +++ b/libs/hwui/shader/RadialGradientShader.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 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. + */ +#include "RadialGradientShader.h" + +#include <vector> + +#include "SkGradientShader.h" + +namespace android::uirenderer { + +RadialGradientShader::RadialGradientShader(const SkPoint& center, const float radius, + const std::vector<SkColor4f>& colors, + sk_sp<SkColorSpace> colorspace, const SkScalar pos[], + const SkTileMode tileMode, const uint32_t shaderFlags, + const SkMatrix* matrix) + : Shader(matrix) + , skShader(SkGradientShader::MakeRadial(center, radius, colors.data(), colorspace, pos, + colors.size(), tileMode, shaderFlags, nullptr)) {} + +sk_sp<SkShader> RadialGradientShader::makeSkShader() { + return skShader; +} + +RadialGradientShader::~RadialGradientShader() {} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/RadialGradientShader.h b/libs/hwui/shader/RadialGradientShader.h new file mode 100644 index 000000000000..9a2ff139aedb --- /dev/null +++ b/libs/hwui/shader/RadialGradientShader.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" + +namespace android::uirenderer { + +/** + * Shader implementation that renders a color ramp from the center outward to either as either + * a SkShader or SkImageFilter + */ +class RadialGradientShader : public Shader { +public: + RadialGradientShader(const SkPoint& center, const float radius, + const std::vector<SkColor4f>& colors, sk_sp<SkColorSpace> colorSpace, + const SkScalar pos[], const SkTileMode tileMode, const uint32_t shaderFlags, + const SkMatrix* matrix); + ~RadialGradientShader() override; + +protected: + sk_sp<SkShader> makeSkShader() override; + +private: + sk_sp<SkShader> skShader; +}; +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/RuntimeShader.cpp b/libs/hwui/shader/RuntimeShader.cpp new file mode 100644 index 000000000000..dd0b6980841a --- /dev/null +++ b/libs/hwui/shader/RuntimeShader.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "RuntimeShader.h" + +#include "SkShader.h" +#include "include/effects/SkRuntimeEffect.h" + +namespace android::uirenderer { + +RuntimeShader::RuntimeShader(SkRuntimeEffect& effect, sk_sp<SkData> data, bool isOpaque, + const SkMatrix* matrix) + : Shader(nullptr) + , // Explicitly passing null as RuntimeShader is created with the + // matrix directly + skShader(effect.makeShader(std::move(data), nullptr, 0, matrix, isOpaque)) {} + +sk_sp<SkShader> RuntimeShader::makeSkShader() { + return skShader; +} + +RuntimeShader::~RuntimeShader() {} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/RuntimeShader.h b/libs/hwui/shader/RuntimeShader.h new file mode 100644 index 000000000000..7fe0b0206467 --- /dev/null +++ b/libs/hwui/shader/RuntimeShader.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" +#include "include/effects/SkRuntimeEffect.h" + +namespace android::uirenderer { + +/** + * RuntimeShader implementation that can map to either a SkShader or SkImageFilter + */ +class RuntimeShader : public Shader { +public: + RuntimeShader(SkRuntimeEffect& effect, sk_sp<SkData> data, bool isOpaque, + const SkMatrix* matrix); + ~RuntimeShader() override; + +protected: + sk_sp<SkShader> makeSkShader() override; + +private: + sk_sp<SkShader> skShader; +}; +} // namespace android::uirenderer diff --git a/libs/hwui/shader/Shader.cpp b/libs/hwui/shader/Shader.cpp new file mode 100644 index 000000000000..45123dd55002 --- /dev/null +++ b/libs/hwui/shader/Shader.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "Shader.h" + +#include "SkImageFilters.h" +#include "SkPaint.h" +#include "SkRefCnt.h" + +namespace android::uirenderer { + +Shader::Shader(const SkMatrix* matrix) + : localMatrix(matrix ? *matrix : SkMatrix::I()) + , skShader(nullptr) + , skImageFilter(nullptr) {} + +Shader::~Shader() {} + +sk_sp<SkShader> Shader::asSkShader() { + // If we already have created a shader with these parameters just return the existing + // shader we have already created + if (!this->skShader.get()) { + this->skShader = makeSkShader(); + if (this->skShader.get()) { + if (!localMatrix.isIdentity()) { + this->skShader = this->skShader->makeWithLocalMatrix(localMatrix); + } + } + } + return this->skShader; +} + +/** + * By default return null as we cannot convert all visual effects to SkShader instances + */ +sk_sp<SkShader> Shader::makeSkShader() { + return nullptr; +} + +sk_sp<SkImageFilter> Shader::asSkImageFilter() { + // If we already have created an ImageFilter with these parameters just return the existing + // ImageFilter we have already created + if (!this->skImageFilter.get()) { + // Attempt to create an SkImageFilter from the current Shader implementation + this->skImageFilter = makeSkImageFilter(); + if (this->skImageFilter) { + if (!localMatrix.isIdentity()) { + // If we have created an SkImageFilter and we have a transformation, wrap + // the created SkImageFilter to apply the given matrix + this->skImageFilter = SkImageFilters::MatrixTransform( + localMatrix, kMedium_SkFilterQuality, this->skImageFilter); + } + } else { + // Otherwise if no SkImageFilter implementation is provided, create one from + // the result of asSkShader. Note the matrix is already applied to the shader in + // this case so just convert it to an SkImageFilter using SkImageFilters::Paint + SkPaint paint; + paint.setShader(asSkShader()); + sk_sp<SkImageFilter> paintFilter = SkImageFilters::Paint(paint); + this->skImageFilter = SkImageFilters::Xfermode(SkBlendMode::kDstIn, + std::move(paintFilter)); + } + } + return this->skImageFilter; +} + +/** + * By default return null for subclasses to implement. If there is not a direct SkImageFilter + * conversion + */ +sk_sp<SkImageFilter> Shader::makeSkImageFilter() { + return nullptr; +} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/Shader.h b/libs/hwui/shader/Shader.h new file mode 100644 index 000000000000..3c0cdaae8253 --- /dev/null +++ b/libs/hwui/shader/Shader.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "SkImageFilter.h" +#include "SkShader.h" +#include "SkPaint.h" +#include "SkRefCnt.h" + +class SkMatrix; + +namespace android::uirenderer { + +/** + * Shader class that can optionally wrap an SkShader or SkImageFilter depending + * on the implementation + */ +class Shader: public SkRefCnt { +public: + /** + * Creates a Shader instance with an optional transformation matrix + * @param matrix Optional matrix to transform the underlying SkShader or SkImageFilter + */ + Shader(const SkMatrix* matrix); + virtual ~Shader(); + + /** + * Create an SkShader from the current Shader instance or return a previously + * created instance. This can be null if no SkShader could be created from this + * Shader instance. + */ + sk_sp<SkShader> asSkShader(); + + /** + * Create an SkImageFilter from the current Shader instance or return a previously + * created instance. Unlike asSkShader, this method cannot return null. + */ + sk_sp<SkImageFilter> asSkImageFilter(); + +protected: + /** + * Create a new SkShader instance based on this Shader instance + */ + virtual sk_sp<SkShader> makeSkShader(); + + /** + * Create a new SkImageFilter instance based on this Shader instance. If no SkImageFilter + * can be created then return nullptr + */ + virtual sk_sp<SkImageFilter> makeSkImageFilter(); + +private: + /** + * Optional matrix transform + */ + const SkMatrix localMatrix; + + /** + * Cached SkShader instance to be returned on subsequent queries + */ + sk_sp<SkShader> skShader; + + /** + * Cached SkImageFilter instance to be returned on subsequent queries + */ + sk_sp<SkImageFilter> skImageFilter; +}; +} // namespace android::uirenderer diff --git a/libs/hwui/shader/SweepGradientShader.cpp b/libs/hwui/shader/SweepGradientShader.cpp new file mode 100644 index 000000000000..3b1f37f8b051 --- /dev/null +++ b/libs/hwui/shader/SweepGradientShader.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "SweepGradientShader.h" + +#include <vector> + +#include "SkGradientShader.h" +#include "SkImageFilters.h" + +namespace android::uirenderer { + +SweepGradientShader::SweepGradientShader(float x, float y, const std::vector<SkColor4f>& colors, + const sk_sp<SkColorSpace>& colorspace, const SkScalar pos[], + const uint32_t shaderFlags, const SkMatrix* matrix) + : Shader(matrix) + , skShader(SkGradientShader::MakeSweep(x, y, colors.data(), colorspace, pos, colors.size(), + shaderFlags, nullptr)) {} + +sk_sp<SkShader> SweepGradientShader::makeSkShader() { + return skShader; +} + +SweepGradientShader::~SweepGradientShader() {} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/SweepGradientShader.h b/libs/hwui/shader/SweepGradientShader.h new file mode 100644 index 000000000000..dad3ef0ffad4 --- /dev/null +++ b/libs/hwui/shader/SweepGradientShader.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" + +namespace android::uirenderer { + +/** + * Shader implementation that renders a color ramp clockwise such that the start and end colors + * are visible at 3 o'clock. This handles converting to either an SkShader or SkImageFilter + */ +class SweepGradientShader : public Shader { +public: + SweepGradientShader(float x, float y, const std::vector<SkColor4f>& colors, + const sk_sp<SkColorSpace>& colorspace, const SkScalar pos[], + const uint32_t shaderFlags, const SkMatrix* matrix); + virtual ~SweepGradientShader() override; + +protected: + virtual sk_sp<SkShader> makeSkShader() override; + +private: + sk_sp<SkShader> skShader; +}; +} // namespace android::uirenderer diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp index c4067af388e3..e2c1651d823a 100644 --- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp +++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp @@ -18,6 +18,7 @@ #include "hwui/Paint.h" #include "TestSceneBase.h" #include "tests/common/BitmapAllocationTestUtils.h" +#include <shader/BitmapShader.h> #include "utils/Color.h" class BitmapShaders; @@ -45,15 +46,24 @@ public: }); Paint paint; + sk_sp<BitmapShader> bitmapShader = sk_make_sp<BitmapShader>( + hwuiBitmap->makeImage(), + SkTileMode::kRepeat, + SkTileMode::kRepeat, + nullptr + ); + sk_sp<SkImage> image = hwuiBitmap->makeImage(); - sk_sp<SkShader> repeatShader = - image->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat); - paint.setShader(std::move(repeatShader)); + paint.setShader(std::move(bitmapShader)); canvas.drawRoundRect(0, 0, 500, 500, 50.0f, 50.0f, paint); - sk_sp<SkShader> mirrorShader = - image->makeShader(SkTileMode::kMirror, SkTileMode::kMirror); - paint.setShader(std::move(mirrorShader)); + sk_sp<BitmapShader> mirrorBitmapShader = sk_make_sp<BitmapShader>( + image, + SkTileMode::kMirror, + SkTileMode::kMirror, + nullptr + ); + paint.setShader(std::move(mirrorBitmapShader)); canvas.drawRoundRect(0, 600, 500, 1100, 50.0f, 50.0f, paint); } diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp index 5886ea39acce..d37bc3c7d37c 100644 --- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp +++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp @@ -20,6 +20,10 @@ #include <SkGradientShader.h> #include <SkImagePriv.h> #include <ui/PixelFormat.h> +#include <shader/BitmapShader.h> +#include <shader/LinearGradientShader.h> +#include <shader/RadialGradientShader.h> +#include <shader/ComposeShader.h> class HwBitmapInCompositeShader; @@ -50,20 +54,41 @@ public: pixels[4000 + 4 * i + 3] = 255; } buffer->unlock(); - sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer->toAHardwareBuffer(), - SkColorSpace::MakeSRGB())); - sk_sp<SkShader> hardwareShader(createBitmapShader(*hardwareBitmap)); + + sk_sp<BitmapShader> bitmapShader = sk_make_sp<BitmapShader>( + Bitmap::createFrom( + buffer->toAHardwareBuffer(), + SkColorSpace::MakeSRGB() + )->makeImage(), + SkTileMode::kClamp, + SkTileMode::kClamp, + nullptr + ); SkPoint center; center.set(50, 50); - SkColor colors[2]; - colors[0] = Color::Black; - colors[1] = Color::White; - sk_sp<SkShader> gradientShader = SkGradientShader::MakeRadial( - center, 50, colors, nullptr, 2, SkTileMode::kRepeat); - - sk_sp<SkShader> compositeShader( - SkShaders::Blend(SkBlendMode::kDstATop, hardwareShader, gradientShader)); + + std::vector<SkColor4f> vColors(2); + vColors[0] = SkColors::kBlack; + vColors[1] = SkColors::kWhite; + + sk_sp<RadialGradientShader> radialShader = sk_make_sp<RadialGradientShader>( + center, + 50, + vColors, + SkColorSpace::MakeSRGB(), + nullptr, + SkTileMode::kRepeat, + 0, + nullptr + ); + + sk_sp<ComposeShader> compositeShader = sk_make_sp<ComposeShader>( + *bitmapShader.get(), + *radialShader.get(), + SkBlendMode::kDstATop, + nullptr + ); Paint paint; paint.setShader(std::move(compositeShader)); diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp index a9449b62a1f8..76e39deedd9a 100644 --- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp @@ -17,7 +17,8 @@ #include "TestSceneBase.h" #include "tests/common/TestListViewSceneBase.h" #include "hwui/Paint.h" -#include <SkGradientShader.h> +#include "SkColor.h" +#include <shader/LinearGradientShader.h> class ListOfFadedTextAnimation; @@ -42,15 +43,26 @@ class ListOfFadedTextAnimation : public TestListViewSceneBase { pts[0].set(0, 0); pts[1].set(0, 1); - SkColor colors[2] = {Color::Black, Color::Transparent}; - sk_sp<SkShader> s( - SkGradientShader::MakeLinear(pts, colors, NULL, 2, SkTileMode::kClamp)); - SkMatrix matrix; matrix.setScale(1, length); matrix.postRotate(-90); + + std::vector<SkColor4f> vColors(2); + vColors[0] = SkColors::kBlack; + vColors[1] = SkColors::kTransparent; + + sk_sp<LinearGradientShader> linearGradientShader = sk_make_sp<LinearGradientShader>( + pts, + vColors, + SkColorSpace::MakeSRGB(), + nullptr, + SkTileMode::kClamp, + 0, + &matrix + ); + Paint fadingPaint; - fadingPaint.setShader(s->makeWithLocalMatrix(matrix)); + fadingPaint.setShader(linearGradientShader); fadingPaint.setBlendMode(SkBlendMode::kDstOut); canvas.drawRect(0, 0, length, itemHeight, fadingPaint); canvas.restore(); diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp index a0bc5aa245d5..bdc157f85264 100644 --- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp @@ -17,7 +17,7 @@ #include "TestSceneBase.h" #include <SkColorMatrixFilter.h> -#include <SkGradientShader.h> +#include <shader/LinearGradientShader.h> class SimpleColorMatrixAnimation; @@ -65,9 +65,12 @@ private: // enough renderer might apply it directly to the paint color) float pos[] = {0, 1}; SkPoint pts[] = {SkPoint::Make(0, 0), SkPoint::Make(width, height)}; - SkColor colors[2] = {Color::DeepPurple_500, Color::DeepOrange_500}; - paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2, - SkTileMode::kClamp)); + std::vector<SkColor4f> colors(2); + colors[0] = SkColor4f::FromColor(Color::DeepPurple_500); + colors[1] = SkColor4f::FromColor(Color::DeepOrange_500); + paint.setShader(sk_make_sp<LinearGradientShader>( + pts, colors, SkColorSpace::MakeSRGB(), pos, SkTileMode::kClamp, + 0, nullptr)); // overdraw several times to emphasize shader cost for (int i = 0; i < 10; i++) { diff --git a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp index 57a260c8d234..9a15c9d370a4 100644 --- a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp @@ -17,6 +17,7 @@ #include "TestSceneBase.h" #include <SkGradientShader.h> +#include <shader/LinearGradientShader.h> class SimpleGradientAnimation; @@ -55,9 +56,24 @@ private: // overdraw several times to emphasize shader cost for (int i = 0; i < 10; i++) { // use i%2 start position to pick 2 color combo with black in it - SkColor colors[3] = {Color::Transparent, Color::Black, Color::Cyan_500}; - paint.setShader(SkGradientShader::MakeLinear(pts, colors + (i % 2), pos, 2, - SkTileMode::kClamp)); + std::vector<SkColor4f> vColors(2); + vColors[0] = ((i % 2) == 0) ? + SkColor4f::FromColor(Color::Transparent) : + SkColor4f::FromColor(Color::Black); + vColors[1] = (((i + 1) % 2) == 0) ? + SkColor4f::FromColor(Color::Black) : + SkColor4f::FromColor(Color::Cyan_500); + + sk_sp<LinearGradientShader> gradient = sk_make_sp<LinearGradientShader>( + pts, + vColors, + SkColorSpace::MakeSRGB(), + pos, + SkTileMode::kClamp, + 0, + nullptr + ); + paint.setShader(gradient); canvas.drawRect(i, i, width, height, paint); } }); diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp index 6d4c57413f00..5e56b26f46f0 100644 --- a/libs/hwui/tests/unit/VectorDrawableTests.cpp +++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp @@ -17,9 +17,14 @@ #include <gtest/gtest.h> #include "PathParser.h" +#include "GraphicsJNI.h" +#include "SkGradientShader.h" +#include "SkShader.h" #include "VectorDrawable.h" #include "utils/MathUtils.h" #include "utils/VectorDrawableUtils.h" +#include <shader/Shader.h> +#include <shader/LinearGradientShader.h> #include <functional> @@ -395,7 +400,21 @@ TEST(VectorDrawable, drawPathWithoutIncrementingShaderRefCount) { bitmap.allocN32Pixels(5, 5, false); SkCanvas canvas(bitmap); - sk_sp<SkShader> shader = SkShaders::Color(SK_ColorBLACK); + SkPoint pts[2]; + pts[0].set(0, 0); + pts[1].set(0, 0); + + std::vector<SkColor4f> colors(2); + colors[0] = SkColors::kBlack; + colors[1] = SkColors::kBlack; + + sk_sp<LinearGradientShader> shader = sk_sp(new LinearGradientShader(pts, + colors, + SkColorSpace::MakeSRGB(), + nullptr, + SkTileMode::kClamp, + SkGradientShader::kInterpolateColorsInPremul_Flag, + nullptr)); // Initial ref count is 1 EXPECT_TRUE(shader->unique()); diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index b2e0538fd4b8..a16e063fe969 100755 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -75,6 +75,7 @@ import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -1599,11 +1600,25 @@ public class AudioManager { @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy, @NonNull AudioDeviceAttributes device) { + return setPreferredDevicesForStrategy(strategy, Arrays.asList(device)); + } + + /** + * @hide + * Removes the preferred audio device(s) previously set with + * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or + * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}. + * @param strategy the audio strategy whose routing will be affected + * @return true if the operation was successful, false otherwise (invalid strategy, or no + * device set for example) + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public boolean removePreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy) { Objects.requireNonNull(strategy); - Objects.requireNonNull(device); try { final int status = - getService().setPreferredDeviceForStrategy(strategy.getId(), device); + getService().removePreferredDevicesForStrategy(strategy.getId()); return status == AudioSystem.SUCCESS; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1612,19 +1627,52 @@ public class AudioManager { /** * @hide - * Removes the preferred audio device previously set with - * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)}. + * Return the preferred device for an audio strategy, previously set with + * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or + * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)} + * @param strategy the strategy to query + * @return the preferred device for that strategy, if multiple devices are set as preferred + * devices, the first one in the list will be returned. Null will be returned if none was + * ever set or if the strategy is invalid + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + @Nullable + public AudioDeviceAttributes getPreferredDeviceForStrategy( + @NonNull AudioProductStrategy strategy) { + List<AudioDeviceAttributes> devices = getPreferredDevicesForStrategy(strategy); + return devices.isEmpty() ? null : devices.get(0); + } + + /** + * @hide + * Set the preferred devices for a given strategy, i.e. the audio routing to be used by + * this audio strategy. Note that the devices may not be available at the time the preferred + * devices is set, but it will be used once made available. + * <p>Use {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} to cancel setting + * this preference for this strategy.</p> + * Note that the list of devices is not a list ranked by preference, but a list of one or more + * devices used simultaneously to output the same audio signal. * @param strategy the audio strategy whose routing will be affected - * @return true if the operation was successful, false otherwise (invalid strategy, or no - * device set for example) + * @param devices a non-empty list of the audio devices to route to when available + * @return true if the operation was successful, false otherwise */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public boolean removePreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy) { + public boolean setPreferredDevicesForStrategy(@NonNull AudioProductStrategy strategy, + @NonNull List<AudioDeviceAttributes> devices) { Objects.requireNonNull(strategy); + Objects.requireNonNull(devices); + if (devices.isEmpty()) { + throw new IllegalArgumentException( + "Tried to set preferred devices for strategy with a empty list"); + } + for (AudioDeviceAttributes device : devices) { + Objects.requireNonNull(device); + } try { final int status = - getService().removePreferredDeviceForStrategy(strategy.getId()); + getService().setPreferredDevicesForStrategy(strategy.getId(), devices); return status == AudioSystem.SUCCESS; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1633,19 +1681,21 @@ public class AudioManager { /** * @hide - * Return the preferred device for an audio strategy, previously set with + * Return the preferred devices for an audio strategy, previously set with * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} + * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)} * @param strategy the strategy to query * @return the preferred device for that strategy, or null if none was ever set or if the * strategy is invalid */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public @Nullable AudioDeviceAttributes getPreferredDeviceForStrategy( + @NonNull + public List<AudioDeviceAttributes> getPreferredDevicesForStrategy( @NonNull AudioProductStrategy strategy) { Objects.requireNonNull(strategy); try { - return getService().getPreferredDeviceForStrategy(strategy.getId()); + return getService().getPreferredDevicesForStrategy(strategy.getId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1657,6 +1707,7 @@ public class AudioManager { * strategy. * <p>Note that this listener will only be invoked whenever * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or + * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)} * {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in * preferred device. It will not be invoked directly after registration with * {@link #addOnPreferredDeviceForStrategyChangedListener(Executor, OnPreferredDeviceForStrategyChangedListener)} @@ -1664,8 +1715,10 @@ public class AudioManager { * @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes) * @see #removePreferredDeviceForStrategy(AudioProductStrategy) * @see #getPreferredDeviceForStrategy(AudioProductStrategy) + * @deprecated use #OnPreferredDevicesForStrategyChangedListener */ @SystemApi + @Deprecated public interface OnPreferredDeviceForStrategyChangedListener { /** * Called on the listener to indicate that the preferred audio device for the given @@ -1680,23 +1733,87 @@ public class AudioManager { /** * @hide + * Interface to be notified of changes in the preferred audio devices set for a given audio + * strategy. + * <p>Note that this listener will only be invoked whenever + * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or + * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)} + * {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in + * preferred device(s). It will not be invoked directly after registration with + * {@link #addOnPreferredDevicesForStrategyChangedListener( + * Executor, OnPreferredDevicesForStrategyChangedListener)} + * to indicate which strategies had preferred devices at the time of registration.</p> + * @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes) + * @see #setPreferredDevicesForStrategy(AudioProductStrategy, List) + * @see #removePreferredDeviceForStrategy(AudioProductStrategy) + * @see #getPreferredDeviceForStrategy(AudioProductStrategy) + * @see #getPreferredDevicesForStrategy(AudioProductStrategy) + */ + @SystemApi + public interface OnPreferredDevicesForStrategyChangedListener { + /** + * Called on the listener to indicate that the preferred audio devices for the given + * strategy has changed. + * @param strategy the {@link AudioProductStrategy} whose preferred device changed + * @param devices a list of newly set preferred audio devices + */ + void onPreferredDevicesForStrategyChanged(@NonNull AudioProductStrategy strategy, + @NonNull List<AudioDeviceAttributes> devices); + } + + /** + * @hide * Adds a listener for being notified of changes to the strategy-preferred audio device. * @param executor * @param listener * @throws SecurityException if the caller doesn't hold the required permission + * @deprecated use {@link #addOnPreferredDevicesForStrategyChangedListener( + * Executor, AudioManager.OnPreferredDevicesForStrategyChangedListener)} instead */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + @Deprecated public void addOnPreferredDeviceForStrategyChangedListener( @NonNull @CallbackExecutor Executor executor, @NonNull OnPreferredDeviceForStrategyChangedListener listener) throws SecurityException { + // No-op, the method is deprecated. + } + + /** + * @hide + * Removes a previously added listener of changes to the strategy-preferred audio device. + * @param listener + * @deprecated use {@link #removeOnPreferredDevicesForStrategyChangedListener( + * AudioManager.OnPreferredDevicesForStrategyChangedListener)} instead + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + @Deprecated + public void removeOnPreferredDeviceForStrategyChangedListener( + @NonNull OnPreferredDeviceForStrategyChangedListener listener) { + // No-op, the method is deprecated. + } + + /** + * @hide + * Adds a listener for being notified of changes to the strategy-preferred audio device. + * @param executor + * @param listener + * @throws SecurityException if the caller doesn't hold the required permission + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public void addOnPreferredDevicesForStrategyChangedListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull OnPreferredDevicesForStrategyChangedListener listener) + throws SecurityException { Objects.requireNonNull(executor); Objects.requireNonNull(listener); synchronized (mPrefDevListenerLock) { if (hasPrefDevListener(listener)) { throw new IllegalArgumentException( - "attempt to call addOnPreferredDeviceForStrategyChangedListener() " + "attempt to call addOnPreferredDevicesForStrategyChangedListener() " + "on a previously registered listener"); } // lazy initialization of the list of strategy-preferred device listener @@ -1708,10 +1825,10 @@ public class AudioManager { if (oldCbCount == 0 && mPrefDevListeners.size() > 0) { // register binder for callbacks if (mPrefDevDispatcherStub == null) { - mPrefDevDispatcherStub = new StrategyPreferredDeviceDispatcherStub(); + mPrefDevDispatcherStub = new StrategyPreferredDevicesDispatcherStub(); } try { - getService().registerStrategyPreferredDeviceDispatcher(mPrefDevDispatcherStub); + getService().registerStrategyPreferredDevicesDispatcher(mPrefDevDispatcherStub); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1726,8 +1843,8 @@ public class AudioManager { */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public void removeOnPreferredDeviceForStrategyChangedListener( - @NonNull OnPreferredDeviceForStrategyChangedListener listener) { + public void removeOnPreferredDevicesForStrategyChangedListener( + @NonNull OnPreferredDevicesForStrategyChangedListener listener) { Objects.requireNonNull(listener); synchronized (mPrefDevListenerLock) { if (!removePrefDevListener(listener)) { @@ -1738,7 +1855,7 @@ public class AudioManager { if (mPrefDevListeners.size() == 0) { // unregister binder for callbacks try { - getService().unregisterStrategyPreferredDeviceDispatcher( + getService().unregisterStrategyPreferredDevicesDispatcher( mPrefDevDispatcherStub); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1760,23 +1877,23 @@ public class AudioManager { private @Nullable ArrayList<PrefDevListenerInfo> mPrefDevListeners; private static class PrefDevListenerInfo { - final @NonNull OnPreferredDeviceForStrategyChangedListener mListener; + final @NonNull OnPreferredDevicesForStrategyChangedListener mListener; final @NonNull Executor mExecutor; - PrefDevListenerInfo(OnPreferredDeviceForStrategyChangedListener listener, Executor exe) { + PrefDevListenerInfo(OnPreferredDevicesForStrategyChangedListener listener, Executor exe) { mListener = listener; mExecutor = exe; } } @GuardedBy("mPrefDevListenerLock") - private StrategyPreferredDeviceDispatcherStub mPrefDevDispatcherStub; + private StrategyPreferredDevicesDispatcherStub mPrefDevDispatcherStub; - private final class StrategyPreferredDeviceDispatcherStub - extends IStrategyPreferredDeviceDispatcher.Stub { + private final class StrategyPreferredDevicesDispatcherStub + extends IStrategyPreferredDevicesDispatcher.Stub { @Override - public void dispatchPrefDeviceChanged(int strategyId, - @Nullable AudioDeviceAttributes device) { + public void dispatchPrefDevicesChanged(int strategyId, + @NonNull List<AudioDeviceAttributes> devices) { // make a shallow copy of listeners so callback is not executed under lock final ArrayList<PrefDevListenerInfo> prefDevListeners; synchronized (mPrefDevListenerLock) { @@ -1791,7 +1908,7 @@ public class AudioManager { try { for (PrefDevListenerInfo info : prefDevListeners) { info.mExecutor.execute(() -> - info.mListener.onPreferredDeviceForStrategyChanged(strategy, device)); + info.mListener.onPreferredDevicesForStrategyChanged(strategy, devices)); } } finally { Binder.restoreCallingIdentity(ident); @@ -1801,7 +1918,7 @@ public class AudioManager { @GuardedBy("mPrefDevListenerLock") private @Nullable PrefDevListenerInfo getPrefDevListenerInfo( - OnPreferredDeviceForStrategyChangedListener listener) { + OnPreferredDevicesForStrategyChangedListener listener) { if (mPrefDevListeners == null) { return null; } @@ -1814,7 +1931,7 @@ public class AudioManager { } @GuardedBy("mPrefDevListenerLock") - private boolean hasPrefDevListener(OnPreferredDeviceForStrategyChangedListener listener) { + private boolean hasPrefDevListener(OnPreferredDevicesForStrategyChangedListener listener) { return getPrefDevListenerInfo(listener) != null; } @@ -1822,7 +1939,7 @@ public class AudioManager { /** * @return true if the listener was removed from the list */ - private boolean removePrefDevListener(OnPreferredDeviceForStrategyChangedListener listener) { + private boolean removePrefDevListener(OnPreferredDevicesForStrategyChangedListener listener) { final PrefDevListenerInfo infoToRemove = getPrefDevListenerInfo(listener); if (infoToRemove != null) { mPrefDevListeners.remove(infoToRemove); diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 5fe5c0580b0c..243ec1f1fcd0 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -32,6 +32,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -1377,6 +1378,11 @@ public class AudioSystem /** @hide */ public static final int FOR_VIBRATE_RINGING = 7; private static final int NUM_FORCE_USE = 8; + // Device role in audio policy + public static final int DEVICE_ROLE_NONE = 0; + public static final int DEVICE_ROLE_PREFERRED = 1; + public static final int DEVICE_ROLE_DISABLED = 2; + /** @hide */ public static String forceUseUsageToString(int usage) { switch (usage) { @@ -1697,47 +1703,58 @@ public class AudioSystem /** * @hide - * Sets the preferred device to use for a given audio strategy in the audio policy engine + * Set device as role for product strategy. * @param strategy the id of the strategy to configure - * @param device the device type and address to route to when available + * @param role the role of the devices + * @param devices the list of devices to be set as role for the given strategy * @return {@link #SUCCESS} if successfully set */ - public static int setPreferredDeviceForStrategy( - int strategy, @NonNull AudioDeviceAttributes device) { - return setPreferredDeviceForStrategy(strategy, - AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()), - device.getAddress()); + public static int setDevicesRoleForStrategy( + int strategy, int role, @NonNull List<AudioDeviceAttributes> devices) { + if (devices.isEmpty()) { + return BAD_VALUE; + } + int[] types = new int[devices.size()]; + String[] addresses = new String[devices.size()]; + for (int i = 0; i < devices.size(); ++i) { + types[i] = AudioDeviceInfo.convertDeviceTypeToInternalDevice(devices.get(i).getType()); + addresses[i] = devices.get(i).getAddress(); + } + return setDevicesRoleForStrategy(strategy, role, types, addresses); } + /** * @hide - * Set device routing per product strategy. + * Set device as role for product strategy. * @param strategy the id of the strategy to configure - * @param deviceType the native device type, NOT AudioDeviceInfo types - * @param deviceAddress the address of the device + * @param role the role of the devices + * @param types all device types + * @param addresses all device addresses * @return {@link #SUCCESS} if successfully set */ - private static native int setPreferredDeviceForStrategy( - int strategy, int deviceType, String deviceAddress); + private static native int setDevicesRoleForStrategy( + int strategy, int role, @NonNull int[] types, @NonNull String[] addresses); /** * @hide - * Remove preferred routing for the strategy + * Remove devices as role for the strategy * @param strategy the id of the strategy to configure + * @param role the role of the devices * @return {@link #SUCCESS} if successfully removed */ - public static native int removePreferredDeviceForStrategy(int strategy); + public static native int removeDevicesRoleForStrategy(int strategy, int role); /** * @hide - * Query previously set preferred device for a strategy + * Query previously set devices as role for a strategy * @param strategy the id of the strategy to query for - * @param device an array of size 1 that will contain the preferred device, or null if - * none was set + * @param role the role of the devices + * @param devices a list that will contain the devices of role * @return {@link #SUCCESS} if there is a preferred device and it was successfully retrieved * and written to the array */ - public static native int getPreferredDeviceForStrategy(int strategy, - AudioDeviceAttributes[] device); + public static native int getDevicesForRoleAndStrategy( + int strategy, int role, @NonNull List<AudioDeviceAttributes> devices); // Items shared with audio service diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 4cf236a84eb0..ef8b0edb1fe5 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -29,7 +29,7 @@ import android.media.IAudioServerStateDispatcher; import android.media.IPlaybackConfigDispatcher; import android.media.IRecordingConfigDispatcher; import android.media.IRingtonePlayer; -import android.media.IStrategyPreferredDeviceDispatcher; +import android.media.IStrategyPreferredDevicesDispatcher; import android.media.IVolumeController; import android.media.IVolumeController; import android.media.PlayerBase; @@ -279,11 +279,11 @@ interface IAudioService { boolean isCallScreeningModeSupported(); - int setPreferredDeviceForStrategy(in int strategy, in AudioDeviceAttributes device); + int setPreferredDevicesForStrategy(in int strategy, in List<AudioDeviceAttributes> device); - int removePreferredDeviceForStrategy(in int strategy); + int removePreferredDevicesForStrategy(in int strategy); - AudioDeviceAttributes getPreferredDeviceForStrategy(in int strategy); + List<AudioDeviceAttributes> getPreferredDevicesForStrategy(in int strategy); List<AudioDeviceAttributes> getDevicesForAttributes(in AudioAttributes attributes); @@ -291,10 +291,10 @@ interface IAudioService { int getAllowedCapturePolicy(); - void registerStrategyPreferredDeviceDispatcher(IStrategyPreferredDeviceDispatcher dispatcher); + void registerStrategyPreferredDevicesDispatcher(IStrategyPreferredDevicesDispatcher dispatcher); - oneway void unregisterStrategyPreferredDeviceDispatcher( - IStrategyPreferredDeviceDispatcher dispatcher); + oneway void unregisterStrategyPreferredDevicesDispatcher( + IStrategyPreferredDevicesDispatcher dispatcher); oneway void setRttEnabled(in boolean rttEnabled); diff --git a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl b/media/java/android/media/IStrategyPreferredDevicesDispatcher.aidl index b1f99e6b729e..db674c36a5c9 100644 --- a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl +++ b/media/java/android/media/IStrategyPreferredDevicesDispatcher.aidl @@ -19,12 +19,12 @@ package android.media; import android.media.AudioDeviceAttributes; /** - * AIDL for AudioService to signal audio strategy-preferred device updates. + * AIDL for AudioService to signal audio strategy-preferred devices updates. * * {@hide} */ -oneway interface IStrategyPreferredDeviceDispatcher { +oneway interface IStrategyPreferredDevicesDispatcher { - void dispatchPrefDeviceChanged(int strategyId, in AudioDeviceAttributes device); + void dispatchPrefDevicesChanged(int strategyId, in List<AudioDeviceAttributes> devices); } diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java index 5d4404036102..e1bff03f6833 100644 --- a/media/java/android/media/MediaTranscodeManager.java +++ b/media/java/android/media/MediaTranscodeManager.java @@ -244,7 +244,7 @@ public final class MediaTranscodeManager { } // Updates the job progress. - job.setJobProgress(newProgress); + job.updateProgress(newProgress); // Notifies client the progress update. if (job.mProgressUpdateExecutor != null && job.mProgressUpdateListener != null) { @@ -254,6 +254,21 @@ public final class MediaTranscodeManager { } } + private void updateStatus(int jobId, int status) { + synchronized (mPendingTranscodingJobs) { + final TranscodingJob job = mPendingTranscodingJobs.get(jobId); + + if (job == null) { + // This should not happen in reality. + Log.e(TAG, "Job " + jobId + " is not in PendingJobs"); + return; + } + + // Updates the job status. + job.updateStatus(status); + } + } + // Just forwards all the events to the event handler. private ITranscodingClientCallback mTranscodingClientCallback = new ITranscodingClientCallback.Stub() { @@ -285,17 +300,17 @@ public final class MediaTranscodeManager { @Override public void onTranscodingStarted(int jobId) throws RemoteException { - + updateStatus(jobId, TranscodingJob.STATUS_RUNNING); } @Override public void onTranscodingPaused(int jobId) throws RemoteException { - + updateStatus(jobId, TranscodingJob.STATUS_PAUSED); } @Override public void onTranscodingResumed(int jobId) throws RemoteException { - + updateStatus(jobId, TranscodingJob.STATUS_RUNNING); } @Override @@ -683,11 +698,14 @@ public final class MediaTranscodeManager { public static final int STATUS_RUNNING = 2; /** The job is finished. */ public static final int STATUS_FINISHED = 3; + /** The job is paused. */ + public static final int STATUS_PAUSED = 4; @IntDef(prefix = { "STATUS_" }, value = { STATUS_PENDING, STATUS_RUNNING, STATUS_FINISHED, + STATUS_PAUSED, }) @Retention(RetentionPolicy.SOURCE) public @interface Status {} @@ -843,9 +861,13 @@ public final class MediaTranscodeManager { return mResult; } - private synchronized void setJobProgress(int newProgress) { + private synchronized void updateProgress(int newProgress) { mProgress = newProgress; } + + private synchronized void updateStatus(int newStatus) { + mStatus = newStatus; + } } /** diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 6976a3569655..20006934ee46 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -523,7 +523,8 @@ public final class MediaSessionManager { * @param keyEvent The KeyEvent to send. * @hide */ - public void dispatchMediaKeyEventAsSystemService(KeyEvent keyEvent) { + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public void dispatchMediaKeyEventAsSystemService(@NonNull KeyEvent keyEvent) { dispatchMediaKeyEventInternal(true, keyEvent, false); } @@ -548,6 +549,7 @@ public final class MediaSessionManager { * @return {@code true} if the event was sent to the session, {@code false} otherwise * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public boolean dispatchMediaKeyEventAsSystemService(@NonNull MediaSession.Token sessionToken, @NonNull KeyEvent keyEvent) { if (sessionToken == null) { @@ -586,10 +588,15 @@ public final class MediaSessionManager { * Should be only called by the {@link com.android.internal.policy.PhoneWindow} or * {@link android.view.FallbackEventHandler} when the foreground activity didn't consume the key * from the hardware devices. + * <p> + * Valid stream types include {@link AudioManager.PublicStreamTypes} and + * {@link AudioManager#USE_DEFAULT_STREAM_TYPE}. * - * @param keyEvent The KeyEvent to send. + * @param keyEvent volume key event + * @param streamType type of stream * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void dispatchVolumeKeyEventAsSystemService(@NonNull KeyEvent keyEvent, int streamType) { dispatchVolumeKeyEventInternal(true, keyEvent, streamType, false); } @@ -614,6 +621,7 @@ public final class MediaSessionManager { * @param keyEvent volume key event * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void dispatchVolumeKeyEventAsSystemService(@NonNull MediaSession.Token sessionToken, @NonNull KeyEvent keyEvent) { if (sessionToken == null) { diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 8bf688dba10b..e148d0e29b5a 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -362,6 +362,11 @@ public class Tuner implements AutoCloseable { */ @Override public void close() { + releaseAll(); + TunerUtils.throwExceptionForResult(nativeClose(), "failed to close tuner"); + } + + private void releaseAll() { if (mFrontendHandle != null) { int res = nativeCloseFrontend(mFrontendHandle); if (res != Tuner.RESULT_SUCCESS) { @@ -396,9 +401,9 @@ public class Tuner implements AutoCloseable { TunerUtils.throwExceptionForResult(res, "failed to close demux"); } mTunerResourceManager.releaseDemux(mDemuxHandle, mClientId); - mFrontendHandle = null; + mDemuxHandle = null; } - TunerUtils.throwExceptionForResult(nativeClose(), "failed to close tuner"); + } /** @@ -495,6 +500,7 @@ public class Tuner implements AutoCloseable { break; } case MSG_RESOURCE_LOST: { + releaseAll(); if (mOnResourceLostListener != null && mOnResourceLostListenerExecutor != null) { mOnResourceLostListenerExecutor.execute( diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java index 39c7682a2a74..1386cba5f9ca 100644 --- a/media/java/android/service/media/MediaBrowserService.java +++ b/media/java/android/service/media/MediaBrowserService.java @@ -684,8 +684,15 @@ public abstract class MediaBrowserService extends Service { List<MediaBrowser.MediaItem> filteredList = (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0 ? applyOptions(list, options) : list; - final ParceledListSlice<MediaBrowser.MediaItem> pls = - filteredList == null ? null : new ParceledListSlice<>(filteredList); + final ParceledListSlice<MediaBrowser.MediaItem> pls; + if (filteredList == null) { + pls = null; + } else { + pls = new ParceledListSlice<>(filteredList); + // Limit the size of initial Parcel to prevent binder buffer overflow + // as onLoadChildren is an async binder call. + pls.setInlineCountLimit(1); + } try { connection.callbacks.onLoadChildren(parentId, pls, options); } catch (RemoteException ex) { diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp index 96961ac21a2d..45c49e58f390 100644 --- a/media/jni/audioeffect/android_media_AudioEffect.cpp +++ b/media/jni/audioeffect/android_media_AudioEffect.cpp @@ -333,7 +333,7 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t if (deviceType != AUDIO_DEVICE_NONE) { device.mType = deviceType; ScopedUtfChars address(env, deviceAddress); - device.mAddress = address.c_str(); + device.setAddress(address.c_str()); } // create the native AudioEffect object diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java index 8e4153078174..8fe10888cab6 100644 --- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java +++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java @@ -33,6 +33,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; @@ -238,6 +239,7 @@ public class MediaTranscodeManagerTest Log.d(TAG, "Starting: testMediaTranscodeManager"); Semaphore transcodeCompleteSemaphore = new Semaphore(0); + final CountDownLatch statusLatch = new CountDownLatch(1); // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4 // The full path of this file is: @@ -276,12 +278,21 @@ public class MediaTranscodeManagerTest public void onProgressUpdate(int newProgress) { assertTrue("Invalid proress update", newProgress > mPreviousProgress); assertTrue("Invalid proress update", newProgress <= 100); + if (newProgress > 0) { + statusLatch.countDown(); + } mPreviousProgress = newProgress; progressUpdateCount.getAndIncrement(); Log.i(TAG, "Get progress update " + newProgress); } }); + try { + statusLatch.await(); + // The transcoding should not be finished yet as the clip is long. + assertTrue("Invalid status", job.getStatus() == TranscodingJob.STATUS_RUNNING); + } catch (InterruptedException e) { } + Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to cancel."); boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire( TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS); diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index e06cf945ddaa..e90fbd4dead8 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -5596,6 +5596,7 @@ package android.app { method public android.graphics.drawable.Icon getIcon(); method public android.app.RemoteInput[] getRemoteInputs(); method public int getSemanticAction(); + method public boolean isAuthenticationRequired(); method public boolean isContextual(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.Notification.Action> CREATOR; @@ -5625,6 +5626,7 @@ package android.app { method @NonNull public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Extender); method @NonNull public android.os.Bundle getExtras(); method @NonNull public android.app.Notification.Action.Builder setAllowGeneratedReplies(boolean); + method @NonNull public android.app.Notification.Action.Builder setAuthenticationRequired(boolean); method @NonNull public android.app.Notification.Action.Builder setContextual(boolean); method @NonNull public android.app.Notification.Action.Builder setSemanticAction(int); } @@ -44124,7 +44126,9 @@ package android.telecom { method public final void addExistingConnection(android.telecom.PhoneAccountHandle, android.telecom.Connection); method public final void conferenceRemoteConnections(android.telecom.RemoteConnection, android.telecom.RemoteConnection); method public final void connectionServiceFocusReleased(); + method @Nullable public final android.telecom.RemoteConference createRemoteIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); method public final android.telecom.RemoteConnection createRemoteIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); + method @Nullable public final android.telecom.RemoteConference createRemoteOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); method public final android.telecom.RemoteConnection createRemoteOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public final java.util.Collection<android.telecom.Conference> getAllConferences(); method public final java.util.Collection<android.telecom.Connection> getAllConnections(); @@ -44355,6 +44359,7 @@ package android.telecom { public final class RemoteConnection { method public void abort(); + method public void addConferenceParticipants(@NonNull java.util.List<android.net.Uri>); method public void answer(); method public void disconnect(); method public android.net.Uri getAddress(); @@ -45132,7 +45137,7 @@ package android.telephony { method public long getNci(); method @IntRange(from=0, to=3279165) public int getNrarfcn(); method @IntRange(from=0, to=1007) public int getPci(); - method @IntRange(from=0, to=65535) public int getTac(); + method @IntRange(from=0, to=16777215) public int getTac(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityNr> CREATOR; } @@ -63865,7 +63870,7 @@ package java.lang.reflect { package java.math { - public class BigDecimal extends java.lang.Number implements java.lang.Comparable<java.math.BigDecimal> java.io.Serializable { + public class BigDecimal extends java.lang.Number implements java.lang.Comparable<java.math.BigDecimal> { ctor public BigDecimal(char[], int, int); ctor public BigDecimal(char[], int, int, java.math.MathContext); ctor public BigDecimal(char[]); @@ -63952,19 +63957,20 @@ package java.math { field public static final java.math.BigDecimal ZERO; } - public class BigInteger extends java.lang.Number implements java.lang.Comparable<java.math.BigInteger> java.io.Serializable { + public class BigInteger extends java.lang.Number implements java.lang.Comparable<java.math.BigInteger> { + ctor public BigInteger(byte[]); + ctor public BigInteger(int, byte[]); + ctor public BigInteger(@NonNull String, int); + ctor public BigInteger(@NonNull String); ctor public BigInteger(int, @NonNull java.util.Random); ctor public BigInteger(int, int, @NonNull java.util.Random); - ctor public BigInteger(@NonNull String); - ctor public BigInteger(@NonNull String, int); - ctor public BigInteger(int, byte[]); - ctor public BigInteger(byte[]); method @NonNull public java.math.BigInteger abs(); method @NonNull public java.math.BigInteger add(@NonNull java.math.BigInteger); method @NonNull public java.math.BigInteger and(@NonNull java.math.BigInteger); method @NonNull public java.math.BigInteger andNot(@NonNull java.math.BigInteger); method public int bitCount(); method public int bitLength(); + method public byte byteValueExact(); method @NonNull public java.math.BigInteger clearBit(int); method public int compareTo(@NonNull java.math.BigInteger); method @NonNull public java.math.BigInteger divide(@NonNull java.math.BigInteger); @@ -63975,8 +63981,10 @@ package java.math { method @NonNull public java.math.BigInteger gcd(@NonNull java.math.BigInteger); method public int getLowestSetBit(); method public int intValue(); + method public int intValueExact(); method public boolean isProbablePrime(int); method public long longValue(); + method public long longValueExact(); method @NonNull public java.math.BigInteger max(@NonNull java.math.BigInteger); method @NonNull public java.math.BigInteger min(@NonNull java.math.BigInteger); method @NonNull public java.math.BigInteger mod(@NonNull java.math.BigInteger); @@ -63993,6 +64001,7 @@ package java.math { method @NonNull public java.math.BigInteger setBit(int); method @NonNull public java.math.BigInteger shiftLeft(int); method @NonNull public java.math.BigInteger shiftRight(int); + method public short shortValueExact(); method public int signum(); method @NonNull public java.math.BigInteger subtract(@NonNull java.math.BigInteger); method public boolean testBit(int); diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt index 35b483b9fb84..8892a2954afb 100644 --- a/non-updatable-api/module-lib-current.txt +++ b/non-updatable-api/module-lib-current.txt @@ -7,6 +7,7 @@ package android.app { 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"; } } @@ -45,6 +46,13 @@ package android.media.session { field public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 65536; // 0x10000 } + public final class MediaSessionManager { + method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent); + method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); + method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int); + method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); + } + } package android.os { diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index 0906a060c40f..3fd0ee1c8011 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -301,6 +301,7 @@ package android { field public static final int config_helpIntentNameKey = 17039390; // 0x104001e field public static final int config_helpPackageNameKey = 17039387; // 0x104001b field public static final int config_helpPackageNameValue = 17039388; // 0x104001c + field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 field public static final int config_systemGallery = 17039399; // 0x1040027 } @@ -4135,7 +4136,8 @@ package android.media { public class AudioManager { method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes); - method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException; + method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException; + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException; method public void clearAudioServerStateCallback(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); @@ -4147,13 +4149,15 @@ package android.media { method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); + method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages(); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method public boolean isAudioServerRunning(); method public boolean isHdmiSystemAudioSupported(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy); method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback); - method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener); + method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException; method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException; @@ -4163,6 +4167,7 @@ package android.media { method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy); @@ -4186,8 +4191,12 @@ package android.media { method public void onAudioServerUp(); } - public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener { - method public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes); + @Deprecated public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener { + method @Deprecated public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes); + } + + public static interface AudioManager.OnPreferredDevicesForStrategyChangedListener { + method public void onPreferredDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>); } public abstract static class AudioManager.VolumeGroupCallback { @@ -11028,6 +11037,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); + field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43 field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0 diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp index 8598f74e1441..08dd79973fa9 100644 --- a/packages/CarSystemUI/Android.bp +++ b/packages/CarSystemUI/Android.bp @@ -128,6 +128,8 @@ android_app { "CarSystemUI-core", ], + export_package_resources: true, + libs: [ "android.car", ], diff --git a/packages/CarSystemUI/proguard.flags b/packages/CarSystemUI/proguard.flags index 66cbf2650429..f0b20c105b84 100644 --- a/packages/CarSystemUI/proguard.flags +++ b/packages/CarSystemUI/proguard.flags @@ -1,4 +1,7 @@ -keep class com.android.systemui.CarSystemUIFactory -keep class com.android.car.notification.headsup.animationhelper.** +-keep class com.android.systemui.DaggerCarGlobalRootComponent { *; } +-keep class com.android.systemui.DaggerCarGlobalRootComponent$CarSysUIComponentImpl { *; } + -include ../SystemUI/proguard.flags diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml index 93174983b116..a49a6373a252 100644 --- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml @@ -25,7 +25,7 @@ <!--The 20dp padding is the difference between the background selected icon size and the ripple that was chosen, thus it's a hack to make it look pretty and not an official margin value--> <LinearLayout - android:id="@id/nav_buttons" + android:id="@+id/nav_buttons" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" diff --git a/packages/CarSystemUI/res/values/ids.xml b/packages/CarSystemUI/res/values/ids.xml index 27ed2e250d9f..05194a4d6279 100644 --- a/packages/CarSystemUI/res/values/ids.xml +++ b/packages/CarSystemUI/res/values/ids.xml @@ -18,5 +18,4 @@ <resources> <!-- Values used for finding elements on the system ui nav bars --> <item type="id" name="lock_screen_nav_buttons"/> - <item type="id" name="nav_buttons"/> </resources>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_apps.xml b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_apps.xml new file mode 100644 index 000000000000..a8d8a2f241f6 --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_apps.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="44" + android:viewportHeight="44" + android:width="44dp" + android:height="44dp"> +<path + android:pathData="M7.33333333 14.6666667L14.6666667 14.6666667L14.6666667 7.33333333L7.33333333 7.33333333L7.33333333 14.6666667ZM18.3333333 36.6666667L25.6666667 36.6666667L25.6666667 29.3333333L18.3333333 29.3333333L18.3333333 36.6666667ZM7.33333333 36.6666667L14.6666667 36.6666667L14.6666667 29.3333333L7.33333333 29.3333333L7.33333333 36.6666667ZM7.33333333 25.6666667L14.6666667 25.6666667L14.6666667 18.3333333L7.33333333 18.3333333L7.33333333 25.6666667ZM18.3333333 25.6666667L25.6666667 25.6666667L25.6666667 18.3333333L18.3333333 18.3333333L18.3333333 25.6666667ZM29.3333333 7.33333333L29.3333333 14.6666667L36.6666667 14.6666667L36.6666667 7.33333333L29.3333333 7.33333333ZM18.3333333 14.6666667L25.6666667 14.6666667L25.6666667 7.33333333L18.3333333 7.33333333L18.3333333 14.6666667ZM29.3333333 25.6666667L36.6666667 25.6666667L36.6666667 18.3333333L29.3333333 18.3333333L29.3333333 25.6666667ZM29.3333333 36.6666667L36.6666667 36.6666667L36.6666667 29.3333333L29.3333333 29.3333333L29.3333333 36.6666667Z" + android:fillColor="@color/car_nav_icon_fill_color" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_apps_selected.xml b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_apps_selected.xml new file mode 100644 index 000000000000..2a4e91aa3cd9 --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_apps_selected.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="44" + android:viewportHeight="44" + android:width="44dp" + android:height="44dp"> + <path + android:pathData="M7.33333333 14.6666667L14.6666667 14.6666667L14.6666667 7.33333333L7.33333333 7.33333333L7.33333333 14.6666667ZM18.3333333 36.6666667L25.6666667 36.6666667L25.6666667 29.3333333L18.3333333 29.3333333L18.3333333 36.6666667ZM7.33333333 36.6666667L14.6666667 36.6666667L14.6666667 29.3333333L7.33333333 29.3333333L7.33333333 36.6666667ZM7.33333333 25.6666667L14.6666667 25.6666667L14.6666667 18.3333333L7.33333333 18.3333333L7.33333333 25.6666667ZM18.3333333 25.6666667L25.6666667 25.6666667L25.6666667 18.3333333L18.3333333 18.3333333L18.3333333 25.6666667ZM29.3333333 7.33333333L29.3333333 14.6666667L36.6666667 14.6666667L36.6666667 7.33333333L29.3333333 7.33333333ZM18.3333333 14.6666667L25.6666667 14.6666667L25.6666667 7.33333333L18.3333333 7.33333333L18.3333333 14.6666667ZM29.3333333 25.6666667L36.6666667 25.6666667L36.6666667 18.3333333L29.3333333 18.3333333L29.3333333 25.6666667ZM29.3333333 36.6666667L36.6666667 36.6666667L36.6666667 29.3333333L29.3333333 29.3333333L29.3333333 36.6666667Z" + android:fillColor="@color/car_nav_icon_fill_color_selected" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_music.xml b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_music.xml new file mode 100644 index 000000000000..6339ebb3ea8d --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_music.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="44" + android:viewportHeight="44" + android:width="44dp" + android:height="44dp"> + <path + android:pathData="M22 5.5L22 24.8416667C20.9183333 24.2183333 19.6716667 23.8333333 18.3333333 23.8333333C14.2816667 23.8333333 11 27.115 11 31.1666667C11 35.2183333 14.2816667 38.5 18.3333333 38.5C22.385 38.5 25.6666667 35.2183333 25.6666667 31.1666667L25.6666667 12.8333333L33 12.8333333L33 5.5L22 5.5Z" + android:fillColor="@color/car_nav_icon_fill_color" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_music_selected.xml b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_music_selected.xml new file mode 100644 index 000000000000..a56bcb38d883 --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_music_selected.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="44" + android:viewportHeight="44" + android:width="44dp" + android:height="44dp"> + <path + android:pathData="M22 5.5L22 24.8416667C20.9183333 24.2183333 19.6716667 23.8333333 18.3333333 23.8333333C14.2816667 23.8333333 11 27.115 11 31.1666667C11 35.2183333 14.2816667 38.5 18.3333333 38.5C22.385 38.5 25.6666667 35.2183333 25.6666667 31.1666667L25.6666667 12.8333333L33 12.8333333L33 5.5L22 5.5Z" + android:fillColor="@color/car_nav_icon_fill_color_selected" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_navigation.xml b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_navigation.xml new file mode 100644 index 000000000000..e1fabe07cdeb --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_navigation.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="44" + android:viewportHeight="44" + android:width="44dp" + android:height="44dp"> + <path + android:pathData="M39.8016667 20.6983333L23.3016667 4.19833333C22.5866667 3.48333333 21.4316667 3.48333333 20.7166667 4.19833333L4.21666667 20.6983333C3.50166667 21.4133333 3.50166667 22.5683333 4.21666667 23.2833333L20.7166667 39.7833333C21.4316667 40.4983333 22.5866667 40.4983333 23.3016667 39.7833333L39.8016667 23.2833333C40.5166667 22.5866667 40.5166667 21.4316667 39.8016667 20.6983333ZM25.6666667 26.5833333L25.6666667 22L18.3333333 22L18.3333333 27.5L14.6666667 27.5L14.6666667 20.1666667C14.6666667 19.1583333 15.4916667 18.3333333 16.5 18.3333333L25.6666667 18.3333333L25.6666667 13.75L32.0833333 20.1666667L25.6666667 26.5833333Z" + android:fillColor="@color/car_nav_icon_fill_color" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_navigation_selected.xml b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_navigation_selected.xml new file mode 100644 index 000000000000..d11cf28f6ca7 --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_navigation_selected.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="44" + android:viewportHeight="44" + android:width="44dp" + android:height="44dp"> + <path + android:pathData="M39.8016667 20.6983333L23.3016667 4.19833333C22.5866667 3.48333333 21.4316667 3.48333333 20.7166667 4.19833333L4.21666667 20.6983333C3.50166667 21.4133333 3.50166667 22.5683333 4.21666667 23.2833333L20.7166667 39.7833333C21.4316667 40.4983333 22.5866667 40.4983333 23.3016667 39.7833333L39.8016667 23.2833333C40.5166667 22.5866667 40.5166667 21.4316667 39.8016667 20.6983333ZM25.6666667 26.5833333L25.6666667 22L18.3333333 22L18.3333333 27.5L14.6666667 27.5L14.6666667 20.1666667C14.6666667 19.1583333 15.4916667 18.3333333 16.5 18.3333333L25.6666667 18.3333333L25.6666667 13.75L32.0833333 20.1666667L25.6666667 26.5833333Z" + android:fillColor="@color/car_nav_icon_fill_color_selected" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_overview.xml b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_overview.xml new file mode 100644 index 000000000000..f185eb9afb75 --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_overview.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="44" + android:viewportHeight="44" + android:width="44dp" + android:height="44dp"> + <path + android:pathData="M36.92857 22.39286A14.53571 14.53571 0 0 1 7.857143 22.39286A14.53571 14.53571 0 0 1 36.92857 22.39286Z" + android:strokeColor="@color/car_nav_icon_fill_color" + android:strokeWidth="4" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_overview_selected.xml b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_overview_selected.xml new file mode 100644 index 000000000000..19b558363720 --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_overview_selected.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="44" + android:viewportHeight="44" + android:width="44dp" + android:height="44dp"> + <path + android:pathData="M36.92857 22.39286A14.53571 14.53571 0 0 1 7.857143 22.39286A14.53571 14.53571 0 0 1 36.92857 22.39286Z" + android:strokeColor="@color/car_nav_icon_fill_color_selected" + android:strokeWidth="4" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_phone.xml b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_phone.xml new file mode 100644 index 000000000000..50e36b5a6e3c --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_phone.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="44" + android:viewportHeight="44" + android:width="44dp" + android:height="44dp"> + <path + android:pathData="M12.1366667 19.7816667C14.7766667 24.97 19.03 29.205 24.2183333 31.8633333L28.2516667 27.83C28.7466667 27.335 29.48 27.17 30.1216667 27.39C32.175 28.0683333 34.3933333 28.435 36.6666667 28.435C37.675 28.435 38.5 29.26 38.5 30.2683333L38.5 36.6666667C38.5 37.675 37.675 38.5 36.6666667 38.5C19.4516667 38.5 5.5 24.5483333 5.5 7.33333333C5.5 6.325 6.325 5.5 7.33333333 5.5L13.75 5.5C14.7583333 5.5 15.5833333 6.325 15.5833333 7.33333333C15.5833333 9.625 15.95 11.825 16.6283333 13.8783333C16.83 14.52 16.6833333 15.235 16.17 15.7483333L12.1366667 19.7816667Z" + android:fillColor="@color/car_nav_icon_fill_color" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_phone_selected.xml b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_phone_selected.xml new file mode 100644 index 000000000000..11b1687cf1c1 --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/drawable/car_ic_phone_selected.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="44" + android:viewportHeight="44" + android:width="44dp" + android:height="44dp"> + <path + android:pathData="M12.1366667 19.7816667C14.7766667 24.97 19.03 29.205 24.2183333 31.8633333L28.2516667 27.83C28.7466667 27.335 29.48 27.17 30.1216667 27.39C32.175 28.0683333 34.3933333 28.435 36.6666667 28.435C37.675 28.435 38.5 29.26 38.5 30.2683333L38.5 36.6666667C38.5 37.675 37.675 38.5 36.6666667 38.5C19.4516667 38.5 5.5 24.5483333 5.5 7.33333333C5.5 6.325 6.325 5.5 7.33333333 5.5L13.75 5.5C14.7583333 5.5 15.5833333 6.325 15.5833333 7.33333333C15.5833333 9.625 15.95 11.825 16.6283333 13.8783333C16.83 14.52 16.6833333 15.235 16.17 15.7483333L12.1366667 19.7816667Z" + android:fillColor="@color/car_nav_icon_fill_color_selected" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/drawable/system_bar_background.xml b/packages/CarSystemUI/samples/sample1/rro/res/drawable/system_bar_background.xml new file mode 100644 index 000000000000..6161ad9b041c --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/drawable/system_bar_background.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > + <corners + android:topLeftRadius="0dp" + android:topRightRadius="10dp" + android:bottomLeftRadius="0dp" + android:bottomRightRadius="0dp" + /> + <solid + android:color="#404040" + /> + <padding + android:left="0dp" + android:top="0dp" + android:right="0dp" + android:bottom="0dp" + /> +</shape>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/drawable/system_bar_background_2.xml b/packages/CarSystemUI/samples/sample1/rro/res/drawable/system_bar_background_2.xml new file mode 100644 index 000000000000..35821426bee3 --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/drawable/system_bar_background_2.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > + <corners + android:topLeftRadius="10dp" + android:topRightRadius="0dp" + android:bottomLeftRadius="0dp" + android:bottomRightRadius="0dp" + /> + <solid + android:color="#404040" + /> + <padding + android:left="0dp" + android:top="0dp" + android:right="0dp" + android:bottom="0dp" + /> +</shape>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/drawable/system_bar_background_3.xml b/packages/CarSystemUI/samples/sample1/rro/res/drawable/system_bar_background_3.xml new file mode 100644 index 000000000000..afa5b32136a8 --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/drawable/system_bar_background_3.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > + <corners + android:topLeftRadius="0dp" + android:topRightRadius="0dp" + android:bottomLeftRadius="10dp" + android:bottomRightRadius="0dp" + /> + <solid + android:color="#404040" + /> + <padding + android:left="0dp" + android:top="0dp" + android:right="0dp" + android:bottom="0dp" + /> +</shape>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/samples/sample1/rro/res/layout/car_navigation_bar.xml new file mode 100644 index 000000000000..4358d977bcc3 --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/layout/car_navigation_bar.xml @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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. + --> +<com.android.systemui.car.navigationbar.CarNavigationBarView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/transparent" + android:orientation="horizontal"> + <!--The 20dp padding is the difference between the background selected icon size and the ripple + that was chosen, thus it's a hack to make it look pretty and not an official margin value--> + <LinearLayout + android:id="@+id/nav_buttons" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/system_bar_background" + android:gravity="center" + android:layoutDirection="ltr" + android:paddingEnd="20dp" + android:paddingStart="20dp"> + + <com.android.systemui.car.navigationbar.CarNavigationButton + android:id="@+id/home" + style="@style/NavigationBarButton" + systemui:componentNames="com.android.car.carlauncher/.CarLauncher" + systemui:icon="@drawable/car_ic_overview" + systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end" + systemui:selectedIcon="@drawable/car_ic_overview_selected" + systemui:highlightWhenSelected="true" + /> + + <com.android.systemui.car.navigationbar.CarNavigationButton + android:id="@+id/maps_nav" + style="@style/NavigationBarButton" + systemui:categories="android.intent.category.APP_MAPS" + systemui:icon="@drawable/car_ic_navigation" + systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MAPS;launchFlags=0x14000000;end" + systemui:selectedIcon="@drawable/car_ic_navigation_selected" + systemui:highlightWhenSelected="true" + /> + + <com.android.systemui.car.navigationbar.CarNavigationButton + android:id="@+id/music_nav" + style="@style/NavigationBarButton" + systemui:categories="android.intent.category.APP_MUSIC" + systemui:icon="@drawable/car_ic_music" + systemui:intent="intent:#Intent;action=android.car.intent.action.MEDIA_TEMPLATE;launchFlags=0x10000000;end" + systemui:packages="com.android.car.media" + systemui:selectedIcon="@drawable/car_ic_music_selected" + systemui:highlightWhenSelected="true" + /> + + <com.android.systemui.car.navigationbar.CarNavigationButton + android:id="@+id/phone_nav" + style="@style/NavigationBarButton" + systemui:icon="@drawable/car_ic_phone" + systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;package=com.android.car.dialer;launchFlags=0x10000000;end" + systemui:packages="com.android.car.dialer" + systemui:selectedIcon="@drawable/car_ic_phone_selected" + systemui:highlightWhenSelected="true" + /> + + <com.android.systemui.car.navigationbar.CarNavigationButton + android:id="@+id/grid_nav" + style="@style/NavigationBarButton" + systemui:componentNames="com.android.car.carlauncher/.AppGridActivity" + systemui:icon="@drawable/car_ic_apps" + systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end" + systemui:selectedIcon="@drawable/car_ic_apps_selected" + systemui:highlightWhenSelected="true" + /> + + </LinearLayout> +</com.android.systemui.car.navigationbar.CarNavigationBarView> diff --git a/packages/CarSystemUI/samples/sample1/rro/res/layout/car_right_navigation_bar.xml b/packages/CarSystemUI/samples/sample1/rro/res/layout/car_right_navigation_bar.xml new file mode 100644 index 000000000000..dc1d0d64a40b --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/layout/car_right_navigation_bar.xml @@ -0,0 +1,141 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2018, 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. +*/ +--> + +<com.android.systemui.car.navigationbar.CarNavigationBarView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:orientation="vertical" + android:baselineAligned="false" + android:background="@android:color/transparent"> + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="110dp" + android:background="@drawable/system_bar_background_3"> + <FrameLayout + android:id="@+id/clock_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_centerInParent="true"> + <com.android.systemui.car.navigationbar.CarNavigationButton + android:id="@+id/qs" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@null" + systemui:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivities$QuickSettingActivity;launchFlags=0x24000000;end" + /> + <com.android.systemui.statusbar.policy.Clock + android:id="@+id/clock" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:elevation="5dp" + android:singleLine="true" + android:textAppearance="@style/TextAppearance.StatusBar.Clock" + /> + </FrameLayout> + </RelativeLayout> + <View + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + /> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="110dp" + android:layout_gravity="bottom" + android:orientation="horizontal" + android:background="@drawable/system_bar_background_2"> + + <FrameLayout + android:id="@+id/left_hvac_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_alignParentStart="true"> + + <com.android.systemui.car.navigationbar.CarNavigationButton + android:id="@+id/hvacleft" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@null" + systemui:broadcast="true" + systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end" + /> + + <com.android.systemui.car.hvac.AnimatedTemperatureView + android:id="@+id/lefttext" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:paddingStart="@*android:dimen/car_padding_4" + android:paddingEnd="16dp" + android:gravity="center_vertical|start" + android:minEms="4" + android:textAppearance="@style/TextAppearance.CarStatus" + systemui:hvacAreaId="49" + systemui:hvacMaxText="Max" + systemui:hvacMaxValue="126" + systemui:hvacMinText="Min" + systemui:hvacMinValue="0" + systemui:hvacPivotOffset="60dp" + systemui:hvacPropertyId="358614275" + systemui:hvacTempFormat="%.0f\u00B0" + /> + </FrameLayout> + <View + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + /> + <FrameLayout + android:id="@+id/right_hvac_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_alignParentEnd="true"> + + <com.android.systemui.car.navigationbar.CarNavigationButton + android:id="@+id/hvacright" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@null" + systemui:broadcast="true" + systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end" + /> + + <com.android.systemui.car.hvac.AnimatedTemperatureView + android:id="@+id/righttext" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:paddingStart="16dp" + android:paddingEnd="@*android:dimen/car_padding_4" + android:gravity="center_vertical|end" + android:minEms="4" + android:textAppearance="@style/TextAppearance.CarStatus" + systemui:hvacAreaId="68" + systemui:hvacMaxText="Max" + systemui:hvacMaxValue="126" + systemui:hvacMinText="Min" + systemui:hvacMinValue="0" + systemui:hvacPivotOffset="60dp" + systemui:hvacPropertyId="358614275" + systemui:hvacTempFormat="%.0f\u00B0" + /> + </FrameLayout> + </LinearLayout> +</com.android.systemui.car.navigationbar.CarNavigationBarView> diff --git a/packages/CarSystemUI/samples/sample1/rro/res/layout/system_icons.xml b/packages/CarSystemUI/samples/sample1/rro/res/layout/system_icons.xml new file mode 100644 index 000000000000..d23579294ce8 --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/layout/system_icons.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 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 + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/system_icons" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center_vertical"> + + <com.android.systemui.statusbar.phone.StatusIconContainer + android:id="@+id/statusIcons" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:paddingEnd="4dp" + android:gravity="center_vertical" + android:orientation="horizontal" + /> +</LinearLayout>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/values/attrs.xml b/packages/CarSystemUI/samples/sample1/rro/res/values/attrs.xml new file mode 100644 index 000000000000..e02f9e6e9a72 --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/values/attrs.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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. + --> + +<resources> + <attr name="broadcast" format="boolean"/> + <attr name="icon" format="reference"/> + <attr name="selectedIcon" format="reference"/> + <attr name="intent" format="string"/> + <attr name="longIntent" format="string"/> + <attr name="componentNames" format="string" /> + <attr name="highlightWhenSelected" format="boolean" /> + <attr name="categories" format="string"/> + <attr name="packages" format="string" /> + + <!-- Custom attributes to configure hvac values --> + <declare-styleable name="AnimatedTemperatureView"> + <attr name="hvacAreaId" format="integer"/> + <attr name="hvacPropertyId" format="integer"/> + <attr name="hvacTempFormat" format="string"/> + <!-- how far away the animations should center around --> + <attr name="hvacPivotOffset" format="dimension"/> + <attr name="hvacMinValue" format="float"/> + <attr name="hvacMaxValue" format="float"/> + <attr name="hvacMinText" format="string|reference"/> + <attr name="hvacMaxText" format="string|reference"/> + <attr name="android:gravity"/> + <attr name="android:minEms"/> + <attr name="android:textAppearance"/> + </declare-styleable> +</resources> diff --git a/packages/CarSystemUI/samples/sample1/rro/res/values/colors.xml b/packages/CarSystemUI/samples/sample1/rro/res/values/colors.xml new file mode 100644 index 000000000000..c32d638681a2 --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/values/colors.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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. + --> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <color name="car_nav_icon_fill_color">#8F8F8F</color> + <color name="car_nav_icon_fill_color_selected">#FFFFFF</color> +</resources> diff --git a/packages/CarSystemUI/samples/sample1/rro/res/values/config.xml b/packages/CarSystemUI/samples/sample1/rro/res/values/config.xml index 854ab7d7e49b..2ec90e95d707 100644 --- a/packages/CarSystemUI/samples/sample1/rro/res/values/config.xml +++ b/packages/CarSystemUI/samples/sample1/rro/res/values/config.xml @@ -17,8 +17,8 @@ <resources> <!-- Configure which system bars should be displayed. --> - <bool name="config_enableTopNavigationBar">true</bool> - <bool name="config_enableLeftNavigationBar">true</bool> + <bool name="config_enableTopNavigationBar">false</bool> + <bool name="config_enableLeftNavigationBar">false</bool> <bool name="config_enableRightNavigationBar">true</bool> <bool name="config_enableBottomNavigationBar">true</bool> @@ -28,8 +28,8 @@ <!-- STATUS_BAR_EXTRA = 2--> <!-- NAVIGATION_BAR_EXTRA = 3--> <integer name="config_topSystemBarType">0</integer> - <integer name="config_leftSystemBarType">2</integer> - <integer name="config_rightSystemBarType">3</integer> + <integer name="config_leftSystemBarType">0</integer> + <integer name="config_rightSystemBarType">0</integer> <integer name="config_bottomSystemBarType">1</integer> <!-- Configure the relative z-order among the system bars. When two system bars overlap (e.g. @@ -40,8 +40,19 @@ RuntimeException, since their placing order cannot be determined. Bars that do not overlap are allowed to have the same z-order. --> <!-- NOTE: If the z-order of a bar is 10 or above, it will also appear on top of HUN's. --> - <integer name="config_topSystemBarZOrder">1</integer> + <integer name="config_topSystemBarZOrder">0</integer> <integer name="config_leftSystemBarZOrder">0</integer> - <integer name="config_rightSystemBarZOrder">0</integer> + <integer name="config_rightSystemBarZOrder">11</integer> <integer name="config_bottomSystemBarZOrder">10</integer> + + <!-- Whether heads-up notifications should be shown on the bottom. If false, heads-up + notifications will be shown pushed to the top of their parent container. If true, they will + be shown pushed to the bottom of their parent container. If true, then should override + config_headsUpNotificationAnimationHelper to use a different AnimationHelper, such as + com.android.car.notification.headsup.animationhelper. + CarHeadsUpNotificationBottomAnimationHelper. --> + <bool name="config_showHeadsUpNotificationOnBottom">true</bool> + + <string name="config_notificationPanelViewMediator" translatable="false"> + com.android.systemui.car.notification.BottomNotificationPanelViewMediator</string> </resources>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/values/dimens.xml b/packages/CarSystemUI/samples/sample1/rro/res/values/dimens.xml new file mode 100644 index 000000000000..cdfed27c64a7 --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/values/dimens.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 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 + --> +<resources> + <dimen name="car_right_navigation_bar_width">280dp</dimen> +</resources> diff --git a/packages/CarSystemUI/samples/sample1/rro/res/values/styles.xml b/packages/CarSystemUI/samples/sample1/rro/res/values/styles.xml new file mode 100644 index 000000000000..136dc3b6df18 --- /dev/null +++ b/packages/CarSystemUI/samples/sample1/rro/res/values/styles.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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. + --> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <style name="TextAppearance.StatusBar.Clock" + parent="@*android:style/TextAppearance.StatusBar.Icon"> + <item name="android:textSize">40sp</item> + <item name="android:fontFamily">sans-serif-regular</item> + <item name="android:textColor">#FFFFFF</item> + </style> + + <style name="NavigationBarButton"> + <item name="android:layout_height">96dp</item> + <item name="android:layout_width">96dp</item> + <item name="android:background">?android:attr/selectableItemBackground</item> + </style> + + <style name="TextAppearance.CarStatus" parent="@android:style/TextAppearance.DeviceDefault"> + <item name="android:textSize">30sp</item> + <item name="android:textColor">#FFFFFF</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml b/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml index 7bcb8e1b43dd..b8e1edc46da7 100644 --- a/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml +++ b/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml @@ -16,10 +16,50 @@ --> <overlay> + <item target="layout/car_navigation_bar" value="@layout/car_navigation_bar"/> + <item target="layout/system_icons" value="@layout/system_icons"/> + <item target="layout/car_right_navigation_bar" value="@layout/car_right_navigation_bar"/> + + <item target="attr/icon" value="@attr/icon"/> + <item target="attr/selectedIcon" value="@attr/selectedIcon"/> + <item target="attr/intent" value="@attr/longIntent"/> + <item target="attr/componentNames" value="@attr/componentNames"/> + <item target="attr/highlightWhenSelected" value="@attr/highlightWhenSelected"/> + <item target="attr/categories" value="@attr/categories"/> + <item target="attr/packages" value="@attr/packages"/> + <item target="attr/hvacAreaId" value="@attr/hvacAreaId"/> + <item target="attr/hvacPropertyId" value="@attr/hvacPropertyId"/> + <item target="attr/hvacTempFormat" value="@attr/hvacTempFormat"/> + <item target="attr/hvacPivotOffset" value="@attr/hvacPivotOffset"/> + <item target="attr/hvacMinValue" value="@attr/hvacMinValue"/> + <item target="attr/hvacMaxValue" value="@attr/hvacMaxValue"/> + <item target="attr/hvacMinText" value="@attr/hvacMinText"/> + <item target="attr/hvacMaxText" value="@attr/hvacMaxText"/> + <!-- start the intent as a broad cast instead of an activity if true--> + <item target="attr/broadcast" value="@attr/broadcast"/> + + <item target="drawable/car_ic_overview" value="@drawable/car_ic_overview" /> + <item target="drawable/car_ic_overview_selected" value="@drawable/car_ic_overview_selected" /> + <item target="drawable/car_ic_apps" value="@drawable/car_ic_apps" /> + <item target="drawable/car_ic_apps_selected" value="@drawable/car_ic_apps_selected" /> + <item target="drawable/car_ic_music" value="@drawable/car_ic_music" /> + <item target="drawable/car_ic_music_selected" value="@drawable/car_ic_music_selected" /> + <item target="drawable/car_ic_phone" value="@drawable/car_ic_phone" /> + <item target="drawable/car_ic_phone_selected" value="@drawable/car_ic_phone_selected" /> + <item target="drawable/car_ic_navigation" value="@drawable/car_ic_navigation" /> + <item target="drawable/car_ic_navigation_selected" value="@drawable/car_ic_navigation_selected" /> + + <item target="dimen/car_right_navigation_bar_width" value="@dimen/car_right_navigation_bar_width" /> + + <item target="style/NavigationBarButton" value="@style/NavigationBarButton"/> + + <item target="color/car_nav_icon_fill_color" value="@color/car_nav_icon_fill_color" /> + <item target="bool/config_enableTopNavigationBar" value="@bool/config_enableTopNavigationBar"/> <item target="bool/config_enableLeftNavigationBar" value="@bool/config_enableLeftNavigationBar"/> <item target="bool/config_enableRightNavigationBar" value="@bool/config_enableRightNavigationBar"/> <item target="bool/config_enableBottomNavigationBar" value="@bool/config_enableBottomNavigationBar"/> + <item target="bool/config_showHeadsUpNotificationOnBottom" value="@bool/config_showHeadsUpNotificationOnBottom"/> <item target="integer/config_topSystemBarType" value="@integer/config_topSystemBarType"/> <item target="integer/config_leftSystemBarType" value="@integer/config_leftSystemBarType"/> @@ -30,4 +70,6 @@ <item target="integer/config_leftSystemBarZOrder" value="@integer/config_leftSystemBarZOrder"/> <item target="integer/config_rightSystemBarZOrder" value="@integer/config_rightSystemBarZOrder"/> <item target="integer/config_bottomSystemBarZOrder" value="@integer/config_bottomSystemBarZOrder"/> + + <item target="string/config_notificationPanelViewMediator" value="@string/config_notificationPanelViewMediator"/> </overlay>
\ No newline at end of file diff --git a/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java new file mode 100644 index 000000000000..552cadfe967e --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 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 com.android.systemui; + +import com.android.systemui.dagger.GlobalRootComponent; + +import javax.inject.Singleton; + +import dagger.Component; + +/** Car subclass for GlobalRootComponent. */ +@Singleton +@Component( + modules = { + CarSysUIComponentModule.class + }) +public interface CarGlobalRootComponent extends GlobalRootComponent { + /** + * Builder for a CarGlobalRootComponent. + */ + @Component.Builder + interface Builder extends GlobalRootComponent.Builder { + CarGlobalRootComponent build(); + } + + @Override + CarSysUIComponent.Builder getSysUIComponent(); +} diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java index ece3bee000f9..b2f98ecde513 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java @@ -18,35 +18,36 @@ package com.android.systemui; import com.android.systemui.dagger.DependencyBinder; import com.android.systemui.dagger.DependencyProvider; +import com.android.systemui.dagger.SysUIComponent; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SystemServicesModule; import com.android.systemui.dagger.SystemUIModule; -import com.android.systemui.dagger.SystemUIRootComponent; import com.android.systemui.onehanded.dagger.OneHandedModule; import com.android.systemui.pip.phone.dagger.PipModule; -import javax.inject.Singleton; +import dagger.Subcomponent; -import dagger.Component; +/** + * Dagger Subcomponent for Core SysUI. + */ +@SysUISingleton +@Subcomponent(modules = { + CarComponentBinder.class, + DependencyProvider.class, + DependencyBinder.class, + PipModule.class, + OneHandedModule.class, + SystemServicesModule.class, + SystemUIModule.class, + CarSystemUIModule.class, + CarSystemUIBinder.class}) +public interface CarSysUIComponent extends SysUIComponent { -@Singleton -@Component( - modules = { - CarComponentBinder.class, - DependencyProvider.class, - DependencyBinder.class, - PipModule.class, - OneHandedModule.class, - SystemServicesModule.class, - SystemUIModule.class, - CarSystemUIModule.class, - CarSystemUIBinder.class - }) -public interface CarSystemUIRootComponent extends SystemUIRootComponent { /** - * Builder for a CarSystemUIRootComponent. + * Builder for a CarSysUIComponent. */ - @Component.Builder - interface Builder extends SystemUIRootComponent.Builder { - CarSystemUIRootComponent build(); + @Subcomponent.Builder + interface Builder extends SysUIComponent.Builder { + CarSysUIComponent build(); } } diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponentModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponentModule.java new file mode 100644 index 000000000000..4de316693c10 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponentModule.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2020 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 com.android.systemui; + +import dagger.Module; + +/** + * Dagger module for including the CarSysUIComponent. + * + * TODO(b/162923491): Remove or otherwise refactor this module. This is a stop gap. + */ +@Module(subcomponents = {CarSysUIComponent.class}) +public abstract class CarSysUIComponentModule { +} diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java index 03ea9418415d..a65edc5477df 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java @@ -19,7 +19,7 @@ package com.android.systemui; import android.content.Context; import android.content.res.Resources; -import com.android.systemui.dagger.SystemUIRootComponent; +import com.android.systemui.dagger.GlobalRootComponent; import java.util.HashSet; import java.util.Set; @@ -30,8 +30,8 @@ import java.util.Set; public class CarSystemUIFactory extends SystemUIFactory { @Override - protected SystemUIRootComponent buildSystemUIRootComponent(Context context) { - return DaggerCarSystemUIRootComponent.builder() + protected GlobalRootComponent buildGlobalRootComponent(Context context) { + return DaggerCarGlobalRootComponent.builder() .context(context) .build(); } diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java index f4e704ea373e..290700fad147 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java @@ -22,24 +22,23 @@ import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME; import android.content.Context; import android.os.Handler; import android.os.PowerManager; -import android.view.IWindowManager; import com.android.keyguard.KeyguardViewController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.car.CarDeviceProvisionedControllerImpl; import com.android.systemui.car.keyguard.CarKeyguardViewController; +import com.android.systemui.car.notification.NotificationShadeWindowControllerImpl; import com.android.systemui.car.statusbar.DozeServiceHost; -import com.android.systemui.car.statusbar.DummyNotificationShadeWindowController; import com.android.systemui.car.volume.CarVolumeDialogComponent; -import com.android.systemui.dagger.SystemUIRootComponent; +import com.android.systemui.dagger.GlobalRootComponent; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.doze.DozeHost; -import com.android.systemui.pip.phone.PipMenuActivity; -import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.EnhancedEstimates; @@ -52,12 +51,12 @@ import com.android.systemui.stackdivider.DividerModule; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl; import com.android.systemui.statusbar.phone.NotificationGroupManager; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.ShadeControllerImpl; import com.android.systemui.statusbar.policy.BatteryController; @@ -66,30 +65,30 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.volume.VolumeDialogComponent; -import com.android.systemui.wm.DisplaySystemBarsController; -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.SystemWindows; -import com.android.wm.shell.common.TransactionPool; +import com.android.systemui.wmshell.CarWMShellModule; import javax.inject.Named; -import javax.inject.Singleton; import dagger.Binds; import dagger.Module; import dagger.Provides; -@Module(includes = {DividerModule.class, QSModule.class}) -public abstract class CarSystemUIModule { +@Module( + includes = { + DividerModule.class, + QSModule.class, + CarWMShellModule.class + }) +abstract class CarSystemUIModule { - @Singleton + @SysUISingleton @Provides @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) static boolean provideAllowNotificationLongPress() { return false; } - @Singleton + @SysUISingleton @Provides static HeadsUpManagerPhone provideHeadsUpManagerPhone( Context context, @@ -101,7 +100,7 @@ public abstract class CarSystemUIModule { groupManager, configurationController); } - @Singleton + @SysUISingleton @Provides @Named(LEAK_REPORT_EMAIL_NAME) static String provideLeakReportEmail() { @@ -109,48 +108,12 @@ public abstract class CarSystemUIModule { } @Provides - @Singleton + @SysUISingleton static Recents provideRecents(Context context, RecentsImplementation recentsImplementation, CommandQueue commandQueue) { return new Recents(context, recentsImplementation, commandQueue); } - @Singleton - @Provides - static TransactionPool provideTransactionPool() { - return new TransactionPool(); - } - - @Singleton - @Provides - static DisplayController providerDisplayController(Context context, @Main Handler handler, - IWindowManager wmService) { - return new DisplayController(context, handler, wmService); - } - - @Singleton - @Provides - static SystemWindows provideSystemWindows(DisplayController displayController, - IWindowManager wmService) { - return new SystemWindows(displayController, wmService); - } - - @Singleton - @Provides - static DisplayImeController provideDisplayImeController(Context context, - IWindowManager wmService, DisplayController displayController, - @Main Handler mainHandler, TransactionPool transactionPool) { - return new DisplaySystemBarsController.Builder(context, wmService, displayController, - mainHandler, transactionPool).build(); - } - - @Singleton - @PipMenuActivityClass - @Provides - static Class<?> providePipMenuActivityClass() { - return PipMenuActivity.class; - } - @Binds abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone); @@ -162,19 +125,20 @@ public abstract class CarSystemUIModule { NotificationLockscreenUserManagerImpl notificationLockscreenUserManager); @Provides - @Singleton + @SysUISingleton static BatteryController provideBatteryController(Context context, EnhancedEstimates enhancedEstimates, PowerManager powerManager, - BroadcastDispatcher broadcastDispatcher, @Main Handler mainHandler, + BroadcastDispatcher broadcastDispatcher, DemoModeController demoModeController, + @Main Handler mainHandler, @Background Handler bgHandler) { BatteryController bC = new BatteryControllerImpl(context, enhancedEstimates, powerManager, - broadcastDispatcher, mainHandler, bgHandler); + broadcastDispatcher, demoModeController, mainHandler, bgHandler); bC.init(); return bC; } @Binds - @Singleton + @SysUISingleton public abstract QSFactory bindQSFactory(QSFactoryImpl qsFactoryImpl); @Binds @@ -188,8 +152,8 @@ public abstract class CarSystemUIModule { abstract ShadeController provideShadeController(ShadeControllerImpl shadeController); @Binds - abstract SystemUIRootComponent bindSystemUIRootComponent( - CarSystemUIRootComponent systemUIRootComponent); + abstract GlobalRootComponent bindGlobalRootComponent( + CarGlobalRootComponent globalRootComponent); @Binds abstract VolumeDialogComponent bindVolumeDialogComponent( @@ -200,6 +164,10 @@ public abstract class CarSystemUIModule { CarKeyguardViewController carKeyguardViewController); @Binds + abstract NotificationShadeWindowController bindNotificationShadeController( + NotificationShadeWindowControllerImpl notificationPanelViewController); + + @Binds abstract DeviceProvisionedController bindDeviceProvisionedController( CarDeviceProvisionedControllerImpl deviceProvisionedController); @@ -208,9 +176,5 @@ public abstract class CarSystemUIModule { CarDeviceProvisionedControllerImpl deviceProvisionedController); @Binds - abstract NotificationShadeWindowController bindNotificationShadeWindowController( - DummyNotificationShadeWindowController notificationShadeWindowController); - - @Binds abstract DozeHost bindDozeHost(DozeServiceHost dozeServiceHost); } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedControllerImpl.java b/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedControllerImpl.java index 09e62d240a72..a2ba880facfe 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedControllerImpl.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedControllerImpl.java @@ -27,17 +27,17 @@ import android.provider.Settings; import com.android.systemui.Dependency; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import javax.inject.Inject; -import javax.inject.Singleton; /** * A controller that monitors the status of SUW progress for each user in addition to the * functionality provided by {@link DeviceProvisionedControllerImpl}. */ -@Singleton +@SysUISingleton public class CarDeviceProvisionedControllerImpl extends DeviceProvisionedControllerImpl implements CarDeviceProvisionedController { private static final Uri USER_SETUP_IN_PROGRESS_URI = Settings.Secure.getUriFor( diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarServiceProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarServiceProvider.java index 80ee37127965..5778d660a672 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/CarServiceProvider.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/CarServiceProvider.java @@ -21,14 +21,15 @@ import android.content.Context; import androidx.annotation.VisibleForTesting; +import com.android.systemui.dagger.SysUISingleton; + import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** Provides a common connection to the car service that can be shared. */ -@Singleton +@SysUISingleton public class CarServiceProvider { private final Context mContext; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/HvacController.java index 236a6a451ea4..a4b6bfc58d3c 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/hvac/HvacController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/HvacController.java @@ -29,6 +29,7 @@ import android.view.View; import android.view.ViewGroup; import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.dagger.SysUISingleton; import java.util.ArrayList; import java.util.HashMap; @@ -40,13 +41,12 @@ import java.util.Objects; import java.util.Set; import javax.inject.Inject; -import javax.inject.Singleton; /** * Manages the connection to the Car service and delegates value changes to the registered * {@link TemperatureView}s */ -@Singleton +@SysUISingleton public class HvacController { public static final String TAG = "HvacController"; private static final boolean DEBUG = true; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java index 51a7245ea5c6..276ddfbc2b4f 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java @@ -38,6 +38,7 @@ import com.android.systemui.car.CarServiceProvider; import com.android.systemui.car.navigationbar.CarNavigationBarController; import com.android.systemui.car.window.OverlayViewController; import com.android.systemui.car.window.OverlayViewGlobalStateController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.FalsingManager; @@ -49,7 +50,6 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.KeyguardStateController; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; @@ -57,7 +57,7 @@ import dagger.Lazy; * Automotive implementation of the {@link KeyguardViewController}. It controls the Keyguard View * that is mounted to the SystemUIOverlayWindow. */ -@Singleton +@SysUISingleton public class CarKeyguardViewController extends OverlayViewController implements KeyguardViewController { private static final String TAG = "CarKeyguardViewController"; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewMediator.java index 5a35c482fb36..155b73e691ef 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewMediator.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewMediator.java @@ -18,15 +18,15 @@ package com.android.systemui.car.keyguard; import com.android.systemui.car.userswitcher.FullScreenUserSwitcherViewController; import com.android.systemui.car.window.OverlayViewMediator; +import com.android.systemui.dagger.SysUISingleton; import javax.inject.Inject; -import javax.inject.Singleton; /** * Manages events originating from the Keyguard service that cause Keyguard or other OverlayWindow * Components to appear or disappear. */ -@Singleton +@SysUISingleton public class CarKeyguardViewMediator implements OverlayViewMediator { private final CarKeyguardViewController mCarKeyguardViewController; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonRoleHolderController.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonRoleHolderController.java index 5c83c025bc20..f8cd20fe8377 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonRoleHolderController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonRoleHolderController.java @@ -30,13 +30,13 @@ import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.car.CarDeviceProvisionedController; +import com.android.systemui.dagger.SysUISingleton; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; -import javax.inject.Singleton; /** * Some CarNavigationButtons can be associated to a {@link RoleManager} role. When they are, it is @@ -46,7 +46,7 @@ import javax.inject.Singleton; * This class monitors the current role holders for each role type and updates the button icon for * this buttons with have this feature enabled. */ -@Singleton +@SysUISingleton public class ButtonRoleHolderController { private static final String TAG = "ButtonRoleHolderController"; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateController.java index eedcfa548e5a..aa6da89e2864 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateController.java @@ -26,13 +26,14 @@ import android.view.Display; import android.view.View; import android.view.ViewGroup; +import com.android.systemui.dagger.SysUISingleton; + import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.inject.Inject; -import javax.inject.Singleton; /** * CarNavigationButtons can optionally have selection state that toggles certain visual indications @@ -42,7 +43,7 @@ import javax.inject.Singleton; * This class controls the selection state of CarNavigationButtons that have opted in to have such * selection state-dependent visual indications. */ -@Singleton +@SysUISingleton public class ButtonSelectionStateController { private final Set<CarNavigationButton> mRegisteredViews = new HashSet<>(); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateListener.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateListener.java index 13617983b23b..d6216ba87d95 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateListener.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateListener.java @@ -19,16 +19,16 @@ package com.android.systemui.car.navigationbar; import android.app.ActivityTaskManager; import android.util.Log; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.shared.system.TaskStackChangeListener; import javax.inject.Inject; -import javax.inject.Singleton; /** * An implementation of TaskStackChangeListener, that listens for changes in the system * task stack and notifies the navigation bar. */ -@Singleton +@SysUISingleton class ButtonSelectionStateListener extends TaskStackChangeListener { private static final String TAG = ButtonSelectionStateListener.class.getSimpleName(); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java index fe26040c5eae..51a883809aab 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java @@ -23,14 +23,14 @@ import android.view.ViewGroup; import androidx.annotation.Nullable; import com.android.systemui.car.hvac.HvacController; +import com.android.systemui.dagger.SysUISingleton; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; /** A single class which controls the navigation bar views. */ -@Singleton +@SysUISingleton public class CarNavigationBarController { private final Context mContext; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java index adf8d4d5acf8..a473bb7423b7 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java @@ -26,12 +26,12 @@ import androidx.annotation.LayoutRes; import com.android.car.ui.FocusParkingView; import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; import javax.inject.Inject; -import javax.inject.Singleton; /** A factory that creates and caches views for navigation bars. */ -@Singleton +@SysUISingleton public class NavigationBarViewFactory { private static final String TAG = NavigationBarViewFactory.class.getSimpleName(); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java index 3527bf93682f..143c444fc4be 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java @@ -29,6 +29,7 @@ import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import java.lang.annotation.ElementType; @@ -40,13 +41,12 @@ import java.util.Map; import java.util.Set; import javax.inject.Inject; -import javax.inject.Singleton; /** * Reads configs for system bars for each side (TOP, BOTTOM, LEFT, and RIGHT) and returns the * corresponding {@link android.view.WindowManager.LayoutParams} per the configuration. */ -@Singleton +@SysUISingleton public class SystemBarConfigs { private static final String TAG = SystemBarConfigs.class.getSimpleName(); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java index 7d353f5acd9a..8468bef1750c 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java @@ -20,16 +20,16 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.car.navigationbar.CarNavigationBarController; import com.android.systemui.car.window.OverlayPanelViewController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.policy.ConfigurationController; import javax.inject.Inject; -import javax.inject.Singleton; /** * Implementation of NotificationPanelViewMediator that sets the notification panel to be opened * from the top navigation bar. */ -@Singleton +@SysUISingleton public class BottomNotificationPanelViewMediator extends NotificationPanelViewMediator { @Inject diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java index d4f720715a69..3b22a30857a9 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java @@ -29,15 +29,15 @@ import com.android.car.notification.R; import com.android.car.notification.headsup.CarHeadsUpNotificationContainer; import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.car.window.OverlayViewGlobalStateController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import javax.inject.Inject; -import javax.inject.Singleton; /** * A controller for SysUI's HUN display. */ -@Singleton +@SysUISingleton public class CarHeadsUpNotificationSystemContainer implements CarHeadsUpNotificationContainer { private final CarDeviceProvisionedController mCarDeviceProvisionedController; private final OverlayViewGlobalStateController mOverlayViewGlobalStateController; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarNotificationModule.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarNotificationModule.java index b7bc63150ea9..8a3bcfc5ef3a 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarNotificationModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarNotificationModule.java @@ -25,8 +25,7 @@ import com.android.car.notification.NotificationClickHandlerFactory; import com.android.car.notification.NotificationDataManager; import com.android.car.notification.headsup.CarHeadsUpNotificationContainer; import com.android.internal.statusbar.IStatusBarService; - -import javax.inject.Singleton; +import com.android.systemui.dagger.SysUISingleton; import dagger.Binds; import dagger.Module; @@ -38,26 +37,26 @@ import dagger.Provides; @Module public abstract class CarNotificationModule { @Provides - @Singleton + @SysUISingleton static NotificationClickHandlerFactory provideNotificationClickHandlerFactory( IStatusBarService barService) { return new NotificationClickHandlerFactory(barService); } @Provides - @Singleton + @SysUISingleton static NotificationDataManager provideNotificationDataManager() { return new NotificationDataManager(); } @Provides - @Singleton + @SysUISingleton static CarUxRestrictionManagerWrapper provideCarUxRestrictionManagerWrapper() { return new CarUxRestrictionManagerWrapper(); } @Provides - @Singleton + @SysUISingleton static CarNotificationListener provideCarNotificationListener(Context context, CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper, CarHeadsUpNotificationManager carHeadsUpNotificationManager, @@ -69,7 +68,7 @@ public abstract class CarNotificationModule { } @Provides - @Singleton + @SysUISingleton static CarHeadsUpNotificationManager provideCarHeadsUpNotificationManager(Context context, NotificationClickHandlerFactory notificationClickHandlerFactory, NotificationDataManager notificationDataManager, diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java index 8d5843635e5f..3b22fdb50765 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java @@ -49,6 +49,7 @@ import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.car.CarServiceProvider; import com.android.systemui.car.window.OverlayPanelViewController; import com.android.systemui.car.window.OverlayViewGlobalStateController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -59,10 +60,9 @@ import com.android.systemui.statusbar.StatusBarState; import java.util.concurrent.Executor; import javax.inject.Inject; -import javax.inject.Singleton; /** View controller for the notification panel. */ -@Singleton +@SysUISingleton public class NotificationPanelViewController extends OverlayPanelViewController implements CommandQueue.Callbacks { diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java index 0c185bae8199..17b6b74014e4 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java @@ -31,16 +31,16 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.car.navigationbar.CarNavigationBarController; import com.android.systemui.car.window.OverlayViewMediator; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.policy.ConfigurationController; import javax.inject.Inject; -import javax.inject.Singleton; /** * The view mediator which attaches the view controller to other elements of the system ui. Disables * drag open behavior of the notification panel from any navigation bar. */ -@Singleton +@SysUISingleton public class NotificationPanelViewMediator implements OverlayViewMediator, ConfigurationController.ConfigurationListener { diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationShadeWindowControllerImpl.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationShadeWindowControllerImpl.java new file mode 100644 index 000000000000..1a1da89f147a --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationShadeWindowControllerImpl.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.car.notification; + +import com.android.systemui.car.window.OverlayViewGlobalStateController; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.statusbar.NotificationShadeWindowController; + +import javax.inject.Inject; + +/** The automotive version of the notification shade window controller. */ +@SysUISingleton +public class NotificationShadeWindowControllerImpl implements + NotificationShadeWindowController { + + private final OverlayViewGlobalStateController mController; + + @Inject + public NotificationShadeWindowControllerImpl(OverlayViewGlobalStateController controller) { + mController = controller; + } + + @Override + public void setForceDozeBrightness(boolean forceDozeBrightness) { + // No-op since dozing is not supported in Automotive devices. + } + + @Override + public void setNotificationShadeFocusable(boolean focusable) { + mController.setWindowFocusable(focusable); + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationVisibilityLogger.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationVisibilityLogger.java index 44c819711bd2..b263f721d29a 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationVisibilityLogger.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationVisibilityLogger.java @@ -24,19 +24,19 @@ import com.android.car.notification.AlertEntry; import com.android.car.notification.NotificationDataManager; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.UiBackground; import java.util.Set; import java.util.concurrent.Executor; import javax.inject.Inject; -import javax.inject.Singleton; /** * Handles notification logging, in particular, logging which notifications are visible and which * are not. */ -@Singleton +@SysUISingleton public class NotificationVisibilityLogger { private static final String TAG = "NotificationVisibilityLogger"; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/PowerManagerHelper.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/PowerManagerHelper.java index 92a11d8db88f..da43c5487623 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/PowerManagerHelper.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/PowerManagerHelper.java @@ -23,14 +23,14 @@ import android.car.hardware.power.CarPowerManager.CarPowerStateListener; import android.util.Log; import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.dagger.SysUISingleton; import javax.inject.Inject; -import javax.inject.Singleton; /** * Helper class for connecting to the {@link CarPowerManager} and listening for power state changes. */ -@Singleton +@SysUISingleton public class PowerManagerHelper { public static final String TAG = "PowerManagerHelper"; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java index 89c9931ac76e..9bc5b74cda6c 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java @@ -20,16 +20,16 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.car.navigationbar.CarNavigationBarController; import com.android.systemui.car.window.OverlayPanelViewController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.policy.ConfigurationController; import javax.inject.Inject; -import javax.inject.Singleton; /** * Implementation of NotificationPanelViewMediator that sets the notification panel to be opened * from the top navigation bar. */ -@Singleton +@SysUISingleton public class TopNotificationPanelViewMediator extends NotificationPanelViewMediator { @Inject diff --git a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppController.java b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppController.java index 6b41b35f7625..b8d6964fa32d 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppController.java @@ -22,14 +22,14 @@ import android.os.RemoteException; import android.util.Log; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controller responsible for detecting unsafe apps. */ -@Singleton +@SysUISingleton public class SideLoadedAppController extends SystemUI { private static final String TAG = SideLoadedAppController.class.getSimpleName(); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java index a2cd0446d1e8..eb32edb27196 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java @@ -29,19 +29,19 @@ import android.util.Log; import com.android.systemui.R; import com.android.systemui.car.CarDeviceProvisionedController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import java.util.Arrays; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * A class that detects unsafe apps. * An app is considered safe if is a system app or installed through allowed sources. */ -@Singleton +@SysUISingleton public class SideLoadedAppDetector { private static final String TAG = SideLoadedAppDetector.class.getSimpleName(); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppStateController.java index 1d66ddaf7aa9..5b4faa152685 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppStateController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppStateController.java @@ -19,13 +19,14 @@ package com.android.systemui.car.sideloaded; import android.util.Log; import android.view.Display; +import com.android.systemui.dagger.SysUISingleton; + import javax.inject.Inject; -import javax.inject.Singleton; /** * Manager responsible for displaying proper UI when an unsafe app is detected. */ -@Singleton +@SysUISingleton public class SideLoadedAppStateController { private static final String TAG = SideLoadedAppStateController.class.getSimpleName(); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DozeServiceHost.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DozeServiceHost.java index d23660c2445d..3fb3cd8833b9 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DozeServiceHost.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DozeServiceHost.java @@ -16,13 +16,13 @@ package com.android.systemui.car.statusbar; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.doze.DozeHost; import javax.inject.Inject; -import javax.inject.Singleton; /** No-op implementation of {@link DozeHost} for use by car sysui, which does not support dozing. */ -@Singleton +@SysUISingleton public class DozeServiceHost implements DozeHost { @Inject diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java deleted file mode 100644 index 13f2b7ed45db..000000000000 --- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2020 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 com.android.systemui.car.statusbar; - -import android.app.IActivityManager; -import android.content.Context; -import android.view.WindowManager; - -import com.android.systemui.car.window.SystemUIOverlayWindowController; -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.keyguard.KeyguardViewMediator; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.phone.BiometricUnlockController; -import com.android.systemui.statusbar.phone.DozeParameters; -import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; -import com.android.systemui.statusbar.policy.ConfigurationController; - -import javax.inject.Inject; -import javax.inject.Singleton; - -/** - * A dummy implementation of {@link NotificationShadeWindowController}. - * - * TODO(b/155711562): This should be replaced with a longer term solution (i.e. separating - * {@link BiometricUnlockController} from the views it depends on). - */ -@Singleton -public class DummyNotificationShadeWindowController extends NotificationShadeWindowController { - private final SystemUIOverlayWindowController mOverlayWindowController; - - @Inject - public DummyNotificationShadeWindowController(Context context, - WindowManager windowManager, IActivityManager activityManager, - DozeParameters dozeParameters, - StatusBarStateController statusBarStateController, - ConfigurationController configurationController, - KeyguardViewMediator keyguardViewMediator, - KeyguardBypassController keyguardBypassController, - SysuiColorExtractor colorExtractor, - DumpManager dumpManager, - SystemUIOverlayWindowController overlayWindowController) { - super(context, windowManager, activityManager, dozeParameters, statusBarStateController, - configurationController, keyguardViewMediator, keyguardBypassController, - colorExtractor, dumpManager); - mOverlayWindowController = overlayWindowController; - } - - @Override - public void setForceDozeBrightness(boolean forceDozeBrightness) { - // No op. - } - - @Override - public void setNotificationShadeFocusable(boolean focusable) { - // The overlay window is the car sysui equivalent of the notification shade. - mOverlayWindowController.setWindowFocusable(focusable); - } -} diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java index 1a8f19e46798..66bfb2d316ae 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java @@ -30,15 +30,15 @@ import com.android.systemui.R; import com.android.systemui.car.CarServiceProvider; import com.android.systemui.car.window.OverlayViewController; import com.android.systemui.car.window.OverlayViewGlobalStateController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controller for {@link R.layout#car_fullscreen_user_switcher}. */ -@Singleton +@SysUISingleton public class FullScreenUserSwitcherViewController extends OverlayViewController { private final Context mContext; private final Resources mResources; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java index 8b399f888eb3..165fe63c7f37 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java @@ -18,16 +18,16 @@ package com.android.systemui.car.userswitcher; import com.android.systemui.car.keyguard.CarKeyguardViewController; import com.android.systemui.car.window.OverlayViewMediator; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; import javax.inject.Inject; -import javax.inject.Singleton; /** * Manages the fullscreen user switcher and it's interactions with the keyguard. */ -@Singleton +@SysUISingleton public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator { private static final String TAG = FullscreenUserSwitcherViewMediator.class.getSimpleName(); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java index 0d77c1341ffb..6178cbd3a599 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java @@ -38,15 +38,15 @@ import com.android.settingslib.drawable.CircleFramedDrawable; import com.android.systemui.R; import com.android.systemui.car.window.OverlayViewController; import com.android.systemui.car.window.OverlayViewGlobalStateController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import javax.inject.Inject; -import javax.inject.Singleton; /** * Handles showing and hiding UserSwitchTransitionView that is mounted to SystemUiOverlayWindow. */ -@Singleton +@SysUISingleton public class UserSwitchTransitionViewController extends OverlayViewController { private static final String TAG = "UserSwitchTransition"; private static final String ENABLE_DEVELOPER_MESSAGE_TRUE = "true"; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/volume/CarVolumeDialogComponent.java b/packages/CarSystemUI/src/com/android/systemui/car/volume/CarVolumeDialogComponent.java index 98d24b1fc0e4..4cdbfa3236c8 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/volume/CarVolumeDialogComponent.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/volume/CarVolumeDialogComponent.java @@ -19,18 +19,19 @@ package com.android.systemui.car.volume; import android.content.Context; import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.plugins.VolumeDialog; import com.android.systemui.volume.VolumeDialogComponent; import com.android.systemui.volume.VolumeDialogControllerImpl; import javax.inject.Inject; -import javax.inject.Singleton; /** * Allows for adding car specific dialog when the volume dialog is created. */ -@Singleton +@SysUISingleton public class CarVolumeDialogComponent extends VolumeDialogComponent { private CarVolumeDialogImpl mCarVolumeDialog; @@ -38,8 +39,9 @@ public class CarVolumeDialogComponent extends VolumeDialogComponent { @Inject public CarVolumeDialogComponent(Context context, KeyguardViewMediator keyguardViewMediator, VolumeDialogControllerImpl volumeDialogController, + DemoModeController demoModeController, CarServiceProvider carServiceProvider) { - super(context, keyguardViewMediator, volumeDialogController); + super(context, keyguardViewMediator, volumeDialogController, demoModeController); mCarVolumeDialog.setCarServiceProvider(carServiceProvider); } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/volume/VolumeUI.java b/packages/CarSystemUI/src/com/android/systemui/car/volume/VolumeUI.java index 03b61e076c73..b0321abfedd4 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/volume/VolumeUI.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/volume/VolumeUI.java @@ -27,6 +27,7 @@ import android.util.Log; import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.volume.VolumeDialogComponent; @@ -34,12 +35,11 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; /** The entry point for controlling the volume ui in cars. */ -@Singleton +@SysUISingleton public class VolumeUI extends SystemUI { private static final String TAG = "VolumeUI"; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java index 2494242c24f0..22b6455dbe2b 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java @@ -27,6 +27,8 @@ import android.view.WindowInsetsController; import androidx.annotation.VisibleForTesting; +import com.android.systemui.dagger.SysUISingleton; + import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -35,7 +37,6 @@ import java.util.SortedMap; import java.util.TreeMap; import javax.inject.Inject; -import javax.inject.Singleton; /** * This controller is responsible for the following: @@ -46,7 +47,7 @@ import javax.inject.Singleton; * global state of SystemUIOverlayWindow. * </ul> */ -@Singleton +@SysUISingleton public class OverlayViewGlobalStateController { private static final boolean DEBUG = false; private static final String TAG = OverlayViewGlobalStateController.class.getSimpleName(); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java index 029bd3702afe..81b1bf930e7e 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java @@ -29,17 +29,17 @@ import android.view.WindowInsets; import android.view.WindowManager; import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.policy.ConfigurationController; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controls the expansion state of the primary window which will contain all of the fullscreen sysui * behavior. This window still has a collapsed state in order to watch for swipe events to expand * this window for the notification panel. */ -@Singleton +@SysUISingleton public class SystemUIOverlayWindowController implements ConfigurationController.ConfigurationListener { diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowManager.java b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowManager.java index 8cca0ed308b2..6395ebff5a41 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowManager.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowManager.java @@ -21,6 +21,7 @@ import android.util.Log; import com.android.systemui.R; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -28,13 +29,12 @@ import java.util.Map; import javax.inject.Inject; import javax.inject.Provider; -import javax.inject.Singleton; /** * Registers {@link OverlayViewMediator}(s) and synchronizes their calls to hide/show {@link * OverlayViewController}(s) to allow for the correct visibility of system bars. */ -@Singleton +@SysUISingleton public class SystemUIOverlayWindowManager extends SystemUI { private static final String TAG = "SystemUIOverlayWM"; private final Map<Class<?>, Provider<OverlayViewMediator>> diff --git a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java new file mode 100644 index 000000000000..2324c3d59155 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.wmshell; + +import android.content.Context; +import android.os.Handler; +import android.view.IWindowManager; + +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.pip.phone.PipMenuActivity; +import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; +import com.android.systemui.wm.DisplaySystemBarsController; +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.TransactionPool; + +import dagger.Module; +import dagger.Provides; + +/** Provides dependencies from {@link com.android.wm.shell} for CarSystemUI. */ +@Module(includes = WMShellBaseModule.class) +public class CarWMShellModule { + @SysUISingleton + @Provides + DisplayImeController provideDisplayImeController(Context context, + IWindowManager wmService, DisplayController displayController, + @Main Handler mainHandler, TransactionPool transactionPool) { + return new DisplaySystemBarsController.Builder(context, wmService, displayController, + mainHandler, transactionPool).build(); + } + + /** TODO(b/150319024): PipMenuActivity will move to a Window */ + @SysUISingleton + @PipMenuActivityClass + @Provides + Class<?> providePipMenuActivityClass() { + return PipMenuActivity.class; + } +} diff --git a/packages/PrintSpooler/res/values-as/strings.xml b/packages/PrintSpooler/res/values-as/strings.xml index a93fceb87959..b6b287ff66aa 100644 --- a/packages/PrintSpooler/res/values-as/strings.xml +++ b/packages/PrintSpooler/res/values-as/strings.xml @@ -47,7 +47,7 @@ <string name="savetopdf_button" msgid="2976186791686924743">"PDFৰ জৰিয়তে ছেভ কৰক"</string> <string name="print_options_expanded" msgid="6944679157471691859">"প্ৰিণ্ট বিকল্পসমূহ বিস্তাৰ কৰা হ’ল"</string> <string name="print_options_collapsed" msgid="7455930445670414332">"প্ৰিণ্ট বিকল্পসমূহ সংকুচিত কৰা হ’ল"</string> - <string name="search" msgid="5421724265322228497">"সন্ধান কৰক"</string> + <string name="search" msgid="5421724265322228497">"Search"</string> <string name="all_printers_label" msgid="3178848870161526399">"সকলো প্ৰিণ্টাৰ"</string> <string name="add_print_service_label" msgid="5356702546188981940">"সেৱা যোগ কৰক"</string> <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"সন্ধান বাকচটো দেখুওৱা হ’ল"</string> diff --git a/packages/PrintSpooler/res/values-bn/strings.xml b/packages/PrintSpooler/res/values-bn/strings.xml index 637becbe23f0..b2e1eed85f0c 100644 --- a/packages/PrintSpooler/res/values-bn/strings.xml +++ b/packages/PrintSpooler/res/values-bn/strings.xml @@ -47,7 +47,7 @@ <string name="savetopdf_button" msgid="2976186791686924743">"পিডিএফ হিসাবে সেভ করুন"</string> <string name="print_options_expanded" msgid="6944679157471691859">"প্রিন্ট বিকল্প প্রসারিত হয়েছে"</string> <string name="print_options_collapsed" msgid="7455930445670414332">"প্রিন্ট বিকল্প সংকুচিত হয়েছে"</string> - <string name="search" msgid="5421724265322228497">"খুঁজুন"</string> + <string name="search" msgid="5421724265322228497">"সার্চ"</string> <string name="all_printers_label" msgid="3178848870161526399">"সমস্ত প্রিন্টার"</string> <string name="add_print_service_label" msgid="5356702546188981940">"পরিষেবা যোগ করুন"</string> <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"সার্চ বাক্স দেখানো হচ্ছে"</string> diff --git a/packages/PrintSpooler/res/values-kn/strings.xml b/packages/PrintSpooler/res/values-kn/strings.xml index 150ede4f8e62..261fe4b0de9a 100644 --- a/packages/PrintSpooler/res/values-kn/strings.xml +++ b/packages/PrintSpooler/res/values-kn/strings.xml @@ -47,7 +47,7 @@ <string name="savetopdf_button" msgid="2976186791686924743">"PDF ಗೆ ಉಳಿಸು"</string> <string name="print_options_expanded" msgid="6944679157471691859">"ಪ್ರಿಂಟ್ ಆಯ್ಕೆಗಳನ್ನು ವಿಸ್ತರಿಸಲಾಗಿದೆ"</string> <string name="print_options_collapsed" msgid="7455930445670414332">"ಪ್ರಿಂಟ್ ಆಯ್ಕೆಗಳನ್ನು ಮುಚ್ಚಲಾಗಿದೆ"</string> - <string name="search" msgid="5421724265322228497">"ಹುಡುಕಿ"</string> + <string name="search" msgid="5421724265322228497">"Search"</string> <string name="all_printers_label" msgid="3178848870161526399">"ಎಲ್ಲಾ ಪ್ರಿಂಟರ್ಗಳು"</string> <string name="add_print_service_label" msgid="5356702546188981940">"ಸೇವೆಯನ್ನು ಸೇರಿಸು"</string> <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"ಹುಡುಕಾಟ ಪೆಟ್ಟಿಗೆಯನ್ನು ತೋರಿಸಲಾಗಿದೆ"</string> diff --git a/packages/PrintSpooler/res/values-ml/strings.xml b/packages/PrintSpooler/res/values-ml/strings.xml index dbcd34b1360d..73af95d2e117 100644 --- a/packages/PrintSpooler/res/values-ml/strings.xml +++ b/packages/PrintSpooler/res/values-ml/strings.xml @@ -47,7 +47,7 @@ <string name="savetopdf_button" msgid="2976186791686924743">"PDF-ൽ സംരക്ഷിക്കുക"</string> <string name="print_options_expanded" msgid="6944679157471691859">"പ്രിന്റ് ചെയ്യാനുള്ള ഓപ്ഷനുകൾ വിപുലീകരിച്ചു"</string> <string name="print_options_collapsed" msgid="7455930445670414332">"പ്രിന്റ് ചെയ്യാനുള്ള ഓപ്ഷനുകൾ ചുരുക്കി"</string> - <string name="search" msgid="5421724265322228497">"തിരയൽ"</string> + <string name="search" msgid="5421724265322228497">"Search"</string> <string name="all_printers_label" msgid="3178848870161526399">"എല്ലാ പ്രിന്ററുകളും"</string> <string name="add_print_service_label" msgid="5356702546188981940">"സേവനം ചേർക്കുക"</string> <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"തിരയൽ ബോക്സ് ദൃശ്യമാക്കിയിരിക്കുന്നു"</string> diff --git a/packages/PrintSpooler/res/values-mr/strings.xml b/packages/PrintSpooler/res/values-mr/strings.xml index 44456b4cae05..4d7e919ad125 100644 --- a/packages/PrintSpooler/res/values-mr/strings.xml +++ b/packages/PrintSpooler/res/values-mr/strings.xml @@ -47,7 +47,7 @@ <string name="savetopdf_button" msgid="2976186791686924743">"पीडीएफ वर सेव्ह करा"</string> <string name="print_options_expanded" msgid="6944679157471691859">"प्रिंट पर्याय विस्तृत झाले"</string> <string name="print_options_collapsed" msgid="7455930445670414332">"प्रिंट पर्याय संक्षिप्त झाले"</string> - <string name="search" msgid="5421724265322228497">"शोध"</string> + <string name="search" msgid="5421724265322228497">"Search"</string> <string name="all_printers_label" msgid="3178848870161526399">"सर्व प्रिंटर"</string> <string name="add_print_service_label" msgid="5356702546188981940">"सेवा जोडा"</string> <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"शोध बॉक्स दर्शविला"</string> diff --git a/packages/PrintSpooler/res/values-or/strings.xml b/packages/PrintSpooler/res/values-or/strings.xml index a1675fa7ca76..7000b95b35d6 100644 --- a/packages/PrintSpooler/res/values-or/strings.xml +++ b/packages/PrintSpooler/res/values-or/strings.xml @@ -47,7 +47,7 @@ <string name="savetopdf_button" msgid="2976186791686924743">"PDFରେ ସେଭ୍ କରନ୍ତୁ"</string> <string name="print_options_expanded" msgid="6944679157471691859">"ପ୍ରିଣ୍ଟ ବିକଳ୍ପକୁ ବଡ଼ କରାଯାଇଛି"</string> <string name="print_options_collapsed" msgid="7455930445670414332">"ପ୍ରିଣ୍ଟ ବିକଳ୍ପକୁ ଛୋଟ କରାଯାଇଛି"</string> - <string name="search" msgid="5421724265322228497">"ସର୍ଚ୍ଚ କରନ୍ତୁ"</string> + <string name="search" msgid="5421724265322228497">"Search"</string> <string name="all_printers_label" msgid="3178848870161526399">"ସମସ୍ତ ପ୍ରିଣ୍ଟର୍"</string> <string name="add_print_service_label" msgid="5356702546188981940">"ସେବା ଯୋଗ କରନ୍ତୁ"</string> <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"ସର୍ଚ୍ଚ ବକ୍ସ ଦେଖାଯାଇଛି"</string> diff --git a/packages/PrintSpooler/res/values-te/strings.xml b/packages/PrintSpooler/res/values-te/strings.xml index b01b50a60f6b..79944bb9994c 100644 --- a/packages/PrintSpooler/res/values-te/strings.xml +++ b/packages/PrintSpooler/res/values-te/strings.xml @@ -47,7 +47,7 @@ <string name="savetopdf_button" msgid="2976186791686924743">"PDF వలె సేవ్ చేయి"</string> <string name="print_options_expanded" msgid="6944679157471691859">"ముద్రణ ఎంపికలు విస్తరించబడ్డాయి"</string> <string name="print_options_collapsed" msgid="7455930445670414332">"ముద్రణ ఎంపికలు కుదించబడ్డాయి"</string> - <string name="search" msgid="5421724265322228497">"వెతుకు"</string> + <string name="search" msgid="5421724265322228497">"సెర్చ్"</string> <string name="all_printers_label" msgid="3178848870161526399">"అన్ని ప్రింటర్లు"</string> <string name="add_print_service_label" msgid="5356702546188981940">"సేవను జోడించు"</string> <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"శోధన పెట్టె చూపబడింది"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index 4b4861a01898..59d8acb82196 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -298,18 +298,12 @@ public class BluetoothEventManager { CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); if (cachedDevice == null) { cachedDevice = mDeviceManager.addDevice(device); - Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice: " - + cachedDevice); + Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice"); } else if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED && !cachedDevice.getDevice().isConnected()) { // Dispatch device add callback to show bonded but // not connected devices in discovery mode dispatchDeviceAdded(cachedDevice); - Log.d(TAG, "DeviceFoundHandler found bonded and not connected device:" - + cachedDevice); - } else { - Log.d(TAG, "DeviceFoundHandler found existing CachedBluetoothDevice:" - + cachedDevice); } cachedDevice.setRssi(rssi); cachedDevice.setJustDiscovered(true); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 287f80472478..4c80b91f300d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -151,8 +151,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> void onProfileStateChanged(LocalBluetoothProfile profile, int newProfileState) { if (BluetoothUtils.D) { - Log.d(TAG, "onProfileStateChanged: profile " + profile + ", device=" + mDevice - + ", newProfileState " + newProfileState); + Log.d(TAG, "onProfileStateChanged: profile " + profile + ", device " + + mDevice.getAlias() + ", newProfileState " + newProfileState); } if (mLocalAdapter.getState() == BluetoothAdapter.STATE_TURNING_OFF) { @@ -290,9 +290,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } public void setHiSyncId(long id) { - if (BluetoothUtils.D) { - Log.d(TAG, "setHiSyncId: mDevice " + mDevice + ", id " + id); - } mHiSyncId = id; } @@ -638,7 +635,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } if (BluetoothUtils.D) { - Log.e(TAG, "updating profiles for " + mDevice.getAlias() + ", " + mDevice); + Log.d(TAG, "updating profiles for " + mDevice.getAlias()); BluetoothClass bluetoothClass = mDevice.getBluetoothClass(); if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString()); diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 9d06c8467e41..72a6074ff89c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -465,7 +465,16 @@ public class LocalMediaManager implements BluetoothCallback { synchronized (mMediaDevicesLock) { mMediaDevices.clear(); mMediaDevices.addAll(devices); - mMediaDevices.addAll(buildDisconnectedBluetoothDevice()); + // Add disconnected bluetooth devices only when phone output device is available. + for (MediaDevice device : devices) { + final int type = device.getDeviceType(); + if (type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE + || type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE + || type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE) { + mMediaDevices.addAll(buildDisconnectedBluetoothDevice()); + break; + } + } } final MediaDevice infoMediaDevice = mInfoMediaManager.getCurrentConnectedDevice(); diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java index b7ae3dca5c16..bc58bfc97718 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java @@ -189,10 +189,12 @@ public class WifiStatusTracker { } } updateStatusLabel(); + mCallback.run(); } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { // Default to -200 as its below WifiManager.MIN_RSSI. updateRssi(intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200)); updateStatusLabel(); + mCallback.run(); } } @@ -218,13 +220,15 @@ public class WifiStatusTracker { return; } NetworkCapabilities networkCapabilities; - final Network currentWifiNetwork = mWifiManager.getCurrentNetwork(); - if (currentWifiNetwork != null && currentWifiNetwork.equals(mDefaultNetwork)) { + isDefaultNetwork = false; + if (mDefaultNetworkCapabilities != null) { + isDefaultNetwork = mDefaultNetworkCapabilities.hasTransport( + NetworkCapabilities.TRANSPORT_WIFI); + } + if (isDefaultNetwork) { // Wifi is connected and the default network. - isDefaultNetwork = true; networkCapabilities = mDefaultNetworkCapabilities; } else { - isDefaultNetwork = false; networkCapabilities = mConnectivityManager.getNetworkCapabilities( mWifiManager.getCurrentNetwork()); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index a654fd47ea12..8e850b25159c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -585,6 +585,7 @@ public class LocalMediaManagerTest { when(device1.getId()).thenReturn(TEST_DEVICE_ID_1); when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); when(device3.getId()).thenReturn(TEST_DEVICE_ID_3); + when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE); when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id"); assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); @@ -683,6 +684,7 @@ public class LocalMediaManagerTest { when(device1.getId()).thenReturn(TEST_DEVICE_ID_1); when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); when(device3.getId()).thenReturn(TEST_DEVICE_ID_3); + when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE); when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id"); assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 4eea8ad92e61..bcd2ff71b57f 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -165,6 +165,7 @@ public class SecureSettings { Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT, Settings.Secure.PEOPLE_STRIP, Settings.Secure.MEDIA_CONTROLS_RESUME, + Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY, @@ -173,5 +174,7 @@ public class SecureSettings { Settings.Secure.TAPS_APP_TO_EXIT, Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, Settings.Secure.PANIC_GESTURE_ENABLED, + Settings.Secure.PANIC_SOUND_ENABLED, + Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index c68ddbdcb5ad..3630f257f583 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -244,6 +244,8 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.TAP_GESTURE, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.PEOPLE_STRIP, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.MEDIA_CONTROLS_RESUME, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.MEDIA_CONTROLS_RESUME_BLOCKED, + COLON_SEPARATED_PACKAGE_LIST_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_MODE, new InclusiveIntegerRangeValidator( Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, @@ -260,5 +262,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.TAPS_APP_TO_EXIT, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.PANIC_GESTURE_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.PANIC_SOUND_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index fa06e14acccb..66be8dd68283 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -773,29 +773,29 @@ class SettingsProtoDumpUtil { Settings.Global.GPU_DEBUG_LAYERS_GLES, GlobalSettingsProto.Gpu.DEBUG_LAYERS_GLES); dumpSetting(s, p, - Settings.Global.GAME_DRIVER_ALL_APPS, - GlobalSettingsProto.Gpu.GAME_DRIVER_ALL_APPS); + Settings.Global.UPDATABLE_DRIVER_ALL_APPS, + GlobalSettingsProto.Gpu.UPDATABLE_DRIVER_ALL_APPS); dumpSetting(s, p, - Settings.Global.GAME_DRIVER_OPT_IN_APPS, - GlobalSettingsProto.Gpu.GAME_DRIVER_OPT_IN_APPS); + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS, + GlobalSettingsProto.Gpu.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS); dumpSetting(s, p, - Settings.Global.GAME_DRIVER_PRERELEASE_OPT_IN_APPS, - GlobalSettingsProto.Gpu.GAME_DRIVER_PRERELEASE_OPT_IN_APPS); + Settings.Global.UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS, + GlobalSettingsProto.Gpu.UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS); dumpSetting(s, p, - Settings.Global.GAME_DRIVER_OPT_OUT_APPS, - GlobalSettingsProto.Gpu.GAME_DRIVER_OPT_OUT_APPS); + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS, + GlobalSettingsProto.Gpu.UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS); dumpSetting(s, p, - Settings.Global.GAME_DRIVER_DENYLIST, - GlobalSettingsProto.Gpu.GAME_DRIVER_DENYLIST); + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST, + GlobalSettingsProto.Gpu.UPDATABLE_DRIVER_PRODUCTION_DENYLIST); dumpSetting(s, p, - Settings.Global.GAME_DRIVER_ALLOWLIST, - GlobalSettingsProto.Gpu.GAME_DRIVER_ALLOWLIST); + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST, + GlobalSettingsProto.Gpu.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST); dumpSetting(s, p, - Settings.Global.GAME_DRIVER_DENYLISTS, - GlobalSettingsProto.Gpu.GAME_DRIVER_DENYLISTS); + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLISTS, + GlobalSettingsProto.Gpu.UPDATABLE_DRIVER_PRODUCTION_DENYLISTS); dumpSetting(s, p, - Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, - GlobalSettingsProto.Gpu.GAME_DRIVER_SPHAL_LIBRARIES); + Settings.Global.UPDATABLE_DRIVER_SPHAL_LIBRARIES, + GlobalSettingsProto.Gpu.UPDATABLE_DRIVER_SPHAL_LIBRARIES); p.end(gpuToken); final long hdmiToken = p.start(GlobalSettingsProto.HDMI); @@ -1976,6 +1976,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, SecureSettingsProto.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS); + dumpSetting(s, p, + Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, + SecureSettingsProto.ADAPTIVE_CONNECTIVITY_ENABLED); final long controlsToken = p.start(SecureSettingsProto.CONTROLS); dumpSetting(s, p, @@ -2028,6 +2031,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Secure.PANIC_GESTURE_ENABLED, SecureSettingsProto.EmergencyResponse.PANIC_GESTURE_ENABLED); + dumpSetting(s, p, + Settings.Secure.PANIC_SOUND_ENABLED, + SecureSettingsProto.EmergencyResponse.PANIC_SOUND_ENABLED); p.end(emergencyResponseToken); dumpSetting(s, p, diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 4bb8f45f331f..0ac3355c4952 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -504,14 +504,14 @@ public class SettingsBackupTest { Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS, Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST, - Settings.Global.GAME_DRIVER_ALL_APPS, - Settings.Global.GAME_DRIVER_OPT_IN_APPS, - Settings.Global.GAME_DRIVER_PRERELEASE_OPT_IN_APPS, - Settings.Global.GAME_DRIVER_OPT_OUT_APPS, - Settings.Global.GAME_DRIVER_DENYLISTS, - Settings.Global.GAME_DRIVER_DENYLIST, - Settings.Global.GAME_DRIVER_ALLOWLIST, - Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, + Settings.Global.UPDATABLE_DRIVER_ALL_APPS, + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS, + Settings.Global.UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS, + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS, + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLISTS, + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST, + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST, + Settings.Global.UPDATABLE_DRIVER_SPHAL_LIBRARIES, Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX, Settings.Global.GPU_DEBUG_LAYER_APP, Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, diff --git a/packages/SoundPicker/res/values-ar/strings.xml b/packages/SoundPicker/res/values-ar/strings.xml index a91795545269..f8844e94815f 100644 --- a/packages/SoundPicker/res/values-ar/strings.xml +++ b/packages/SoundPicker/res/values-ar/strings.xml @@ -25,5 +25,5 @@ <string name="delete_ringtone_text" msgid="201443984070732499">"حذف"</string> <string name="unable_to_add_ringtone" msgid="4583511263449467326">"يتعذر إضافة نغمة رنين مخصصة"</string> <string name="unable_to_delete_ringtone" msgid="6792301380142859496">"يتعذر حذف نغمة الرنين المخصصة"</string> - <string name="app_label" msgid="3091611356093417332">"Sounds"</string> + <string name="app_label" msgid="3091611356093417332">"الأصوات"</string> </resources> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 98d3553287d1..4ce9f5a9edc6 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -240,6 +240,8 @@ <!-- Listen app op changes --> <uses-permission android:name="android.permission.WATCH_APPOPS" /> <uses-permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" /> + <!-- For handling silent audio recordings --> + <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" /> <!-- to read and change hvac values in a car --> <uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" /> @@ -267,6 +269,7 @@ <!-- Permission to make accessibility service access Bubbles --> <uses-permission android:name="android.permission.ADD_TRUSTED_DISPLAY" /> + <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" /> diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java index d2112a0ce759..883f4de1149c 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java @@ -75,11 +75,6 @@ public interface NotificationMenuRowPlugin extends Plugin { public MenuItem getLongpressMenuItem(Context context); /** - * @return the {@link MenuItem} to display when app ops icons are pressed. - */ - public MenuItem getAppOpsMenuItem(Context context); - - /** * @return the {@link MenuItem} to display when feedback icon is pressed. */ public MenuItem getFeedbackMenuItem(Context context); diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java index 0d960f0c21be..6c5c4ef94921 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java @@ -39,6 +39,10 @@ public interface StatusBarStateController { */ boolean isDozing(); + /** + * Is the status bar panel expanded. + */ + boolean isExpanded(); /** * Is device pulsing. @@ -113,5 +117,10 @@ public interface StatusBarStateController { * Callback to be notified when the pulsing state changes */ default void onPulsingChanged(boolean pulsing) {} + + /** + * Callback to be notified when the expanded state of the status bar changes + */ + default void onExpandedChanged(boolean isExpanded) {} } } diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags index 14097b12e730..b42d71abaa6d 100644 --- a/packages/SystemUI/proguard.flags +++ b/packages/SystemUI/proguard.flags @@ -1,9 +1,9 @@ --keep class com.android.systemui.statusbar.policy.KeyButtonView { +-keep class com.android.systemui.navigationbar.buttons.KeyButtonView { public float getDrawingAlpha(); public void setDrawingAlpha(float); } --keep class com.android.systemui.statusbar.policy.KeyButtonRipple { +-keep class com.android.systemui.navigationbar.buttons.KeyButtonRipple { public float getGlowAlpha(); public float getGlowScale(); public void setGlowAlpha(float); @@ -41,4 +41,8 @@ public <init>(android.content.Context); } --keep class com.android.wm.shell.*
\ No newline at end of file +-keep class com.android.wm.shell.* + +-keep class com.android.systemui.dagger.GlobalRootComponent { *; } +-keep class com.android.systemui.dagger.GlobalRootComponent$SysUIComponentImpl { *; } +-keep class com.android.systemui.dagger.Dagger** { *; }
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/back.xml b/packages/SystemUI/res/layout/back.xml index 4e8726b3a634..046aecda44b3 100644 --- a/packages/SystemUI/res/layout/back.xml +++ b/packages/SystemUI/res/layout/back.xml @@ -14,7 +14,7 @@ limitations under the License. --> -<com.android.systemui.statusbar.policy.KeyButtonView +<com.android.systemui.navigationbar.buttons.KeyButtonView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/back" diff --git a/packages/SystemUI/res/layout/contextual.xml b/packages/SystemUI/res/layout/contextual.xml index 90a776884699..2cd7926b6e46 100644 --- a/packages/SystemUI/res/layout/contextual.xml +++ b/packages/SystemUI/res/layout/contextual.xml @@ -24,7 +24,7 @@ android:clipChildren="false" android:clipToPadding="false" > - <com.android.systemui.statusbar.policy.KeyButtonView + <com.android.systemui.navigationbar.buttons.KeyButtonView android:id="@+id/menu" android:layout_height="match_parent" android:layout_width="match_parent" @@ -47,7 +47,7 @@ android:layout_height="match_parent" android:visibility="invisible" /> - <com.android.systemui.statusbar.policy.KeyButtonView + <com.android.systemui.navigationbar.buttons.KeyButtonView android:id="@+id/accessibility_button" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/packages/SystemUI/res/layout/custom_key.xml b/packages/SystemUI/res/layout/custom_key.xml index 0b5cb7267610..dc65777542e2 100644 --- a/packages/SystemUI/res/layout/custom_key.xml +++ b/packages/SystemUI/res/layout/custom_key.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<com.android.systemui.statusbar.policy.KeyButtonView +<com.android.systemui.navigationbar.buttons.KeyButtonView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:layout_width="@dimen/navigation_side_padding" diff --git a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml index 46396e3e62b4..4b3534b1cbc8 100644 --- a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml +++ b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml @@ -32,6 +32,7 @@ android:gravity="center"> <ImageView android:id="@+id/screenshot_action_chip_icon" + android:tint="@*android:color/accent_device_default" android:layout_width="@dimen/screenshot_action_chip_icon_size" android:layout_height="@dimen/screenshot_action_chip_icon_size" android:layout_marginStart="@dimen/screenshot_action_chip_padding_start" diff --git a/packages/SystemUI/res/layout/home.xml b/packages/SystemUI/res/layout/home.xml index 95863272b9bf..84eed6a233f8 100644 --- a/packages/SystemUI/res/layout/home.xml +++ b/packages/SystemUI/res/layout/home.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<com.android.systemui.statusbar.policy.KeyButtonView +<com.android.systemui.navigationbar.buttons.KeyButtonView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/home" diff --git a/packages/SystemUI/res/layout/home_handle.xml b/packages/SystemUI/res/layout/home_handle.xml index 54a0b9fba39c..c9d3f987902c 100644 --- a/packages/SystemUI/res/layout/home_handle.xml +++ b/packages/SystemUI/res/layout/home_handle.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> -<com.android.systemui.statusbar.phone.NavigationHandle +<com.android.systemui.navigationbar.gestural.NavigationHandle xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/home_handle" android:layout_width="@dimen/navigation_home_handle_width" diff --git a/packages/SystemUI/res/layout/ime_switcher.xml b/packages/SystemUI/res/layout/ime_switcher.xml index 7710b25d6e4c..a2c8308b7f70 100644 --- a/packages/SystemUI/res/layout/ime_switcher.xml +++ b/packages/SystemUI/res/layout/ime_switcher.xml @@ -15,7 +15,7 @@ ~ limitations under the License --> -<com.android.systemui.statusbar.policy.KeyButtonView +<com.android.systemui.navigationbar.buttons.KeyButtonView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ime_switcher" android:layout_width="@dimen/navigation_key_width" diff --git a/packages/SystemUI/res/layout/menu_ime.xml b/packages/SystemUI/res/layout/menu_ime.xml index df717f601763..0bb622bb9c4f 100644 --- a/packages/SystemUI/res/layout/menu_ime.xml +++ b/packages/SystemUI/res/layout/menu_ime.xml @@ -25,7 +25,7 @@ are placed inside a view that has a size controlled by weight. Ensure weight is large enough to support icon size. Use layout_width=navigation_side_padding like other navbar buttons. --> - <com.android.systemui.statusbar.policy.KeyButtonView + <com.android.systemui.navigationbar.buttons.KeyButtonView android:id="@+id/menu" android:layout_width="match_parent" android:layout_height="match_parent" @@ -43,7 +43,7 @@ android:paddingStart="0dp" android:paddingEnd="0dp" /> - <com.android.systemui.statusbar.policy.KeyButtonView + <com.android.systemui.navigationbar.buttons.KeyButtonView android:id="@+id/rotate_suggestion" android:layout_width="match_parent" android:layout_height="match_parent" @@ -51,7 +51,7 @@ android:scaleType="centerInside" android:contentDescription="@string/accessibility_rotate_button" /> - <com.android.systemui.statusbar.policy.KeyButtonView + <com.android.systemui.navigationbar.buttons.KeyButtonView android:id="@+id/accessibility_button" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml index ba6b6956f187..23f36a98ed3e 100644 --- a/packages/SystemUI/res/layout/navigation_bar.xml +++ b/packages/SystemUI/res/layout/navigation_bar.xml @@ -17,9 +17,10 @@ */ --> -<com.android.systemui.statusbar.phone.NavigationBarView +<com.android.systemui.navigationbar.NavigationBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:id="@+id/navigation_bar_view" android:layout_height="match_parent" android:layout_width="match_parent" android:background="@drawable/system_bar_background"> @@ -39,9 +40,9 @@ android:rotation="180" android:visibility="gone"/> - <com.android.systemui.statusbar.phone.NavigationBarInflaterView + <com.android.systemui.navigationbar.NavigationBarInflaterView android:id="@+id/navigation_inflater" android:layout_width="match_parent" android:layout_height="match_parent" /> -</com.android.systemui.statusbar.phone.NavigationBarView> +</com.android.systemui.navigationbar.NavigationBarView> diff --git a/packages/SystemUI/res/layout/navigation_bar_window.xml b/packages/SystemUI/res/layout/navigation_bar_window.xml index f98cbd8d10fa..b2473cd82998 100644 --- a/packages/SystemUI/res/layout/navigation_bar_window.xml +++ b/packages/SystemUI/res/layout/navigation_bar_window.xml @@ -16,7 +16,7 @@ ** limitations under the License. */ --> -<com.android.systemui.statusbar.phone.NavigationBarFrame +<com.android.systemui.navigationbar.NavigationBarFrame xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/navigation_bar_frame" @@ -24,4 +24,4 @@ android:layout_height="match_parent" android:layout_width="match_parent"> -</com.android.systemui.statusbar.phone.NavigationBarFrame> +</com.android.systemui.navigationbar.NavigationBarFrame> diff --git a/packages/SystemUI/res/layout/navigation_layout.xml b/packages/SystemUI/res/layout/navigation_layout.xml index db1c79d24c54..0e576fbe2245 100644 --- a/packages/SystemUI/res/layout/navigation_layout.xml +++ b/packages/SystemUI/res/layout/navigation_layout.xml @@ -25,7 +25,7 @@ android:paddingEnd="@dimen/nav_content_padding" android:id="@+id/horizontal"> - <com.android.systemui.statusbar.phone.NearestTouchFrame + <com.android.systemui.navigationbar.buttons.NearestTouchFrame android:id="@+id/nav_buttons" android:layout_width="match_parent" android:layout_height="match_parent" @@ -50,6 +50,6 @@ android:clipToPadding="false" android:clipChildren="false" /> - </com.android.systemui.statusbar.phone.NearestTouchFrame> + </com.android.systemui.navigationbar.buttons.NearestTouchFrame> </FrameLayout> diff --git a/packages/SystemUI/res/layout/navigation_layout_vertical.xml b/packages/SystemUI/res/layout/navigation_layout_vertical.xml index 285c5c4e0a01..4b6770042632 100644 --- a/packages/SystemUI/res/layout/navigation_layout_vertical.xml +++ b/packages/SystemUI/res/layout/navigation_layout_vertical.xml @@ -25,14 +25,14 @@ android:paddingBottom="@dimen/nav_content_padding" android:id="@+id/vertical"> - <com.android.systemui.statusbar.phone.NearestTouchFrame + <com.android.systemui.navigationbar.buttons.NearestTouchFrame android:id="@+id/nav_buttons" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false"> - <com.android.systemui.statusbar.phone.ReverseLinearLayout + <com.android.systemui.navigationbar.buttons.ReverseLinearLayout android:id="@+id/ends_group" android:layout_width="match_parent" android:layout_height="match_parent" @@ -40,7 +40,7 @@ android:clipToPadding="false" android:clipChildren="false" /> - <com.android.systemui.statusbar.phone.ReverseLinearLayout + <com.android.systemui.navigationbar.buttons.ReverseLinearLayout android:id="@+id/center_group" android:layout_width="match_parent" android:layout_height="match_parent" @@ -49,6 +49,6 @@ android:clipToPadding="false" android:clipChildren="false" /> - </com.android.systemui.statusbar.phone.NearestTouchFrame> + </com.android.systemui.navigationbar.buttons.NearestTouchFrame> </FrameLayout> diff --git a/packages/SystemUI/res/layout/recent_apps.xml b/packages/SystemUI/res/layout/recent_apps.xml index 870bcf7547a7..e2b1374b6a78 100644 --- a/packages/SystemUI/res/layout/recent_apps.xml +++ b/packages/SystemUI/res/layout/recent_apps.xml @@ -14,7 +14,7 @@ limitations under the License. --> -<com.android.systemui.statusbar.policy.KeyButtonView +<com.android.systemui.navigationbar.buttons.KeyButtonView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/recent_apps" diff --git a/packages/SystemUI/res/layout/rotate_suggestion.xml b/packages/SystemUI/res/layout/rotate_suggestion.xml index d7f67db0390c..194d2e063e97 100644 --- a/packages/SystemUI/res/layout/rotate_suggestion.xml +++ b/packages/SystemUI/res/layout/rotate_suggestion.xml @@ -15,7 +15,7 @@ ~ limitations under the License --> -<com.android.systemui.statusbar.policy.KeyButtonView +<com.android.systemui.navigationbar.buttons.KeyButtonView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/rotate_suggestion" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml index 732758a2ded2..31a33fbfc308 100644 --- a/packages/SystemUI/res/layout/udfps_view.xml +++ b/packages/SystemUI/res/layout/udfps_view.xml @@ -5,6 +5,6 @@ android:id="@+id/udfps_view" android:layout_width="match_parent" android:layout_height="match_parent" - systemui:sensorRadius="140px" - systemui:sensorMarginBottom="630px" + systemui:sensorRadius="130px" + systemui:sensorCenterY="1636px" systemui:sensorTouchAreaCoefficient="0.5"/> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index aedbded46298..43ab1d2d4fa1 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -73,7 +73,7 @@ <string name="usb_contaminant_message" msgid="7730476585174719805">"መሣሪያዎን ከፈሳሽ ወይም ፍርስራሽ ለመጠበቅ ሲባል የዩኤስቢ ወደቡ ተሰናክሏል፣ እና ማናቸውም ተቀጥላዎችን አያገኝም።\n\nየዩኤስቢ ወደቡን እንደገና መጠቀም ችግር በማይኖረው ጊዜ ማሳወቂያ ይደርሰዎታል።"</string> <string name="usb_port_enabled" msgid="531823867664717018">"ኃይል መሙያዎችን እና ተጨማሪ መሣሪያዎችን ፈልጎ ለማግኘት የነቃ የዩኤስቢ ወደብ"</string> <string name="usb_disable_contaminant_detection" msgid="3827082183595978641">"ዩኤስቢ አንቃ"</string> - <string name="learn_more" msgid="4690632085667273811">"የበለጠ መረዳት"</string> + <string name="learn_more" msgid="4690632085667273811">"የበለጠ ለመረዳት"</string> <string name="compat_mode_on" msgid="4963711187149440884">"ማያ እንዲሞላ አጉላ"</string> <string name="compat_mode_off" msgid="7682459748279487945">"ማያ ለመሙለት ሳብ"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"ቅጽበታዊ ገጽ እይታ"</string> @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ምክሮችን በመጫን ላይ"</string> <string name="controls_media_title" msgid="1746947284862928133">"ሚዲያ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"የአሁኑን ክፍለ-ጊዜ ደብቅ።"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"አሰናብት"</string> <string name="controls_media_resume" msgid="1933520684481586053">"ከቆመበት ቀጥል"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ቅንብሮች"</string> <string name="controls_error_timeout" msgid="794197289772728958">"ንቁ ያልኾነ፣ መተግበሪያን ይፈትሹ"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index fee86ca42fe6..c68d0887e425 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -1093,8 +1093,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"جارٍ تحميل الاقتراحات"</string> <string name="controls_media_title" msgid="1746947284862928133">"الوسائط"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"إخفاء الجلسة الحالية"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"إغلاق"</string> <string name="controls_media_resume" msgid="1933520684481586053">"استئناف التشغيل"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"الإعدادات"</string> <string name="controls_error_timeout" msgid="794197289772728958">"غير نشط، تحقّق من التطبيق."</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 3778df80197b..d604fd47c4fd 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -125,7 +125,7 @@ <string name="accessibility_accessibility_button" msgid="4089042473497107709">"দিব্যাংগসকলৰ বাবে থকা সুবিধাসমূহ"</string> <string name="accessibility_rotate_button" msgid="1238584767612362586">"স্ক্ৰীণ ঘূৰাওক"</string> <string name="accessibility_recent" msgid="901641734769533575">"অৱলোকন"</string> - <string name="accessibility_search_light" msgid="524741790416076988">"সন্ধান কৰক"</string> + <string name="accessibility_search_light" msgid="524741790416076988">"Search"</string> <string name="accessibility_camera_button" msgid="2938898391716647247">"কেমেৰা"</string> <string name="accessibility_phone_button" msgid="4256353121703100427">"ফ\'ন"</string> <string name="accessibility_voice_assist_button" msgid="6497706615649754510">"কণ্ঠধ্বনিৰে সহায়"</string> @@ -441,7 +441,7 @@ <string name="expanded_header_battery_charging_with_time" msgid="757991461445765011">"বেটাৰিৰ চ্চাৰ্জ সম্পূর্ণ হ\'বলৈ <xliff:g id="CHARGING_TIME">%s</xliff:g> বাকী"</string> <string name="expanded_header_battery_not_charging" msgid="809409140358955848">"চ্চার্জ কৰি থকা নাই"</string> <string name="ssl_ca_cert_warning" msgid="8373011375250324005">"নেটৱৰ্ক \nনিৰীক্ষণ কৰা হ\'ব পাৰে"</string> - <string name="description_target_search" msgid="3875069993128855865">"অনুসন্ধান কৰক"</string> + <string name="description_target_search" msgid="3875069993128855865">"Search"</string> <string name="description_direction_up" msgid="3632251507574121434">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>ৰ বাবে ওপৰলৈ শ্লাইড কৰক।"</string> <string name="description_direction_left" msgid="4762708739096907741">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>ৰ বাবে বাওঁফাললৈ শ্লাইড কৰক।"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"আপুনি নিৰ্দিষ্ট কৰা এলাৰ্ম, ৰিমাইণ্ডাৰ, ইভেন্ট আৰু কল কৰোঁতাৰ বাহিৰে আন কোনো শব্দৰ পৰা আপুনি অসুবিধা নাপাব। কিন্তু, সংগীত, ভিডিঅ\' আৰু খেলসমূহকে ধৰি আপুনি প্লে কৰিব খোজা যিকোনো বস্তু তথাপি শুনিব পাৰিব।"</string> @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"চুপাৰিছসমূহ ল’ড কৰি থকা হৈছে"</string> <string name="controls_media_title" msgid="1746947284862928133">"মিডিয়া"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"বৰ্তমানৰ ছেশ্বনটো লুকুৱাওক।"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"অগ্ৰাহ্য কৰক"</string> <string name="controls_media_resume" msgid="1933520684481586053">"পুনৰ আৰম্ভ কৰক"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ছেটিংসমূহ"</string> <string name="controls_error_timeout" msgid="794197289772728958">"সক্ৰিয় নহয়, এপ্টো পৰীক্ষা কৰক"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 874bf7465827..b1df2522a513 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Tövsiyələr yüklənir"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Cari sessiyanı gizlədin."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"İmtina edin"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Davam edin"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Aktiv deyil, tətbiqi yoxlayın"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 2a120c57e955..f3c4c6b8487d 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -1075,8 +1075,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavaju se preporuke"</string> <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrijte aktuelnu sesiju."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Podešavanja"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno. Vidite aplikaciju"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index e4bb67ae65c6..9c0eeb206e9e 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -1081,8 +1081,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Загружаюцца рэкамендацыі"</string> <string name="controls_media_title" msgid="1746947284862928133">"Мультымедыя"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Схаваць цяперашні сеанс."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Адхіліць"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Узнавіць"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Налады"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Неактыўна, праверце праграму"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index c51e58ccd6cf..3bf12ad06f1a 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Препоръките се зареждат"</string> <string name="controls_media_title" msgid="1746947284862928133">"Мултимедия"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Скриване на текущата сесия."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Отхвърляне"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Възобновяване"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Неактивно, проверете прилож."</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 3d00ca858ef5..5b7c953d5692 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -125,7 +125,7 @@ <string name="accessibility_accessibility_button" msgid="4089042473497107709">"অ্যাক্সেসযোগ্যতা"</string> <string name="accessibility_rotate_button" msgid="1238584767612362586">"স্ক্রিন ঘোরান"</string> <string name="accessibility_recent" msgid="901641734769533575">"এক নজরে"</string> - <string name="accessibility_search_light" msgid="524741790416076988">"খুঁজুন"</string> + <string name="accessibility_search_light" msgid="524741790416076988">"সার্চ"</string> <string name="accessibility_camera_button" msgid="2938898391716647247">"ক্যামেরা"</string> <string name="accessibility_phone_button" msgid="4256353121703100427">"ফোন"</string> <string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ভয়েস সহায়তা"</string> @@ -441,7 +441,7 @@ <string name="expanded_header_battery_charging_with_time" msgid="757991461445765011">"পূর্ণ হতে <xliff:g id="CHARGING_TIME">%s</xliff:g> সময় লাগবে"</string> <string name="expanded_header_battery_not_charging" msgid="809409140358955848">"চার্জ হচ্ছে না"</string> <string name="ssl_ca_cert_warning" msgid="8373011375250324005">"নেটওয়ার্ক নিরীক্ষণ\nকরা হতে পারে"</string> - <string name="description_target_search" msgid="3875069993128855865">"খুঁজুন"</string> + <string name="description_target_search" msgid="3875069993128855865">"সার্চ"</string> <string name="description_direction_up" msgid="3632251507574121434">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> এর জন্য উপরের দিকে স্লাইড করুন৷"</string> <string name="description_direction_left" msgid="4762708739096907741">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> এর জন্য বাঁ দিকে স্লাইড করুন৷"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"অ্যালার্ম, রিমাইন্ডার, ইভেন্ট, এবং আপনার নির্দিষ্ট করে দেওয়া ব্যক্তিদের কল ছাড়া অন্য কোনও আওয়াজ বা ভাইব্রেশন হবে না। তবে সঙ্গীত, ভিডিও, এবং গেম সহ আপনি যা কিছু চালাবেন তার আওয়াজ শুনতে পাবেন।"</string> @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"সাজেশন লোড করা হচ্ছে"</string> <string name="controls_media_title" msgid="1746947284862928133">"মিডিয়া"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"বর্তমান সেশন লুকান।"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"খারিজ করুন"</string> <string name="controls_media_resume" msgid="1933520684481586053">"আবার চালু করুন"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"সেটিংস"</string> <string name="controls_error_timeout" msgid="794197289772728958">"বন্ধ আছে, অ্যাপ চেক করুন"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 14259840d503..31f88c8fbd24 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -1075,8 +1075,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavanje preporuka"</string> <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrijte trenutnu sesiju."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, vidite aplikaciju"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index b1501558f924..753e25f13420 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregant les recomanacions"</string> <string name="controls_media_title" msgid="1746947284862928133">"Multimèdia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Amaga la sessió actual."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignora"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Reprèn"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuració"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Inactiu; comprova l\'aplicació"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index ae6cc13cd1df..039500d2a4f4 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -1081,8 +1081,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Načítání doporučení"</string> <string name="controls_media_title" msgid="1746947284862928133">"Média"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Skrýt aktuální relaci."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Zavřít"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Pokračovat"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavení"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivní, zkontrolujte aplikaci"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 09f7f1a8762e..0c8a5b75c9e6 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Indlæser anbefalinger"</string> <string name="controls_media_title" msgid="1746947284862928133">"Medie"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Skjul den aktuelle session."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Luk"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Genoptag"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Indstillinger"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv. Tjek appen"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 2410e4785c8d..1c4c7ab229f9 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Empfehlungen werden geladen"</string> <string name="controls_media_title" msgid="1746947284862928133">"Medien"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Du kannst die aktuelle Sitzung ausblenden."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ablehnen"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Fortsetzen"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Einstellungen"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv – sieh in der App nach"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index f66c5a88b102..07ff80706b33 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Φόρτωση προτάσεων"</string> <string name="controls_media_title" msgid="1746947284862928133">"Μέσα"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Απόκρυψη της τρέχουσας περιόδου λειτουργίας."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Παράβλεψη"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Συνέχιση"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Ρυθμίσεις"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Ανενεργό, έλεγχος εφαρμογής"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 45e072496b25..38fa52874512 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendaciones"</string> <string name="controls_media_title" msgid="1746947284862928133">"Contenido multimedia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Oculta la sesión actual."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Descartar"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Inactivo. Verifica la app"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index f7ebbd02708a..10aa6cac7c2a 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendaciones"</string> <string name="controls_media_title" msgid="1746947284862928133">"Multimedia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar la sesión."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Cerrar"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Ajustes"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Inactivo, comprobar aplicación"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 1e5e49e0a143..9122ce5c4c0d 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Soovituste laadimine"</string> <string name="controls_media_title" msgid="1746947284862928133">"Meedia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Peidetakse praegune seanss."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Loobu"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Jätka"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Seaded"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Passiivne, vaadake rakendust"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index fa076c4fc40e..52e5e8cf29d6 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Ladataan suosituksia"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Piilota nykyinen käyttökerta."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ohita"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Jatka"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Asetukset"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Epäaktiivinen, tarkista sovellus"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 9c418f79a075..4797f323b5d5 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Chargement des recommandations…"</string> <string name="controls_media_title" msgid="1746947284862928133">"Commandes multimédias"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Masquer la session en cours."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Fermer"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Délai expiré, vérifiez l\'appli"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 3e2f5fdbff1d..e5df728c34c6 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Chargement des recommandations"</string> <string name="controls_media_title" msgid="1746947284862928133">"Multimédia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Masquer la session en cours."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Fermer"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Délai expiré, vérifier l\'appli"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 9ecd75e80a4c..246fefd316db 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendacións"</string> <string name="controls_media_title" msgid="1746947284862928133">"Contido multimedia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Oculta a sesión actual."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignorar"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Inactivo. Comproba a app"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 6e05a540a13e..beaacaa1c073 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -125,7 +125,7 @@ <string name="accessibility_accessibility_button" msgid="4089042473497107709">"ઍક્સેસિબિલિટી"</string> <string name="accessibility_rotate_button" msgid="1238584767612362586">"સ્ક્રીન ફેરવો"</string> <string name="accessibility_recent" msgid="901641734769533575">"ઝલક"</string> - <string name="accessibility_search_light" msgid="524741790416076988">"શોધો"</string> + <string name="accessibility_search_light" msgid="524741790416076988">"શોધ"</string> <string name="accessibility_camera_button" msgid="2938898391716647247">"કૅમેરો"</string> <string name="accessibility_phone_button" msgid="4256353121703100427">"ફોન"</string> <string name="accessibility_voice_assist_button" msgid="6497706615649754510">"વૉઇસ સહાય"</string> @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"સુઝાવ લોડ કરી રહ્યાં છીએ"</string> <string name="controls_media_title" msgid="1746947284862928133">"મીડિયા"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"હાલનું સત્ર છુપાવો."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"છોડી દો"</string> <string name="controls_media_resume" msgid="1933520684481586053">"ફરી શરૂ કરો"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"સેટિંગ"</string> <string name="controls_error_timeout" msgid="794197289772728958">"નિષ્ક્રિય, ઍપને ચેક કરો"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 4bebe79c3c44..7bf0b93b9bcb 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -1075,8 +1075,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavanje preporuka"</string> <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrij trenutačnu sesiju."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, provjerite aplik."</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 42e251fb39de..875a05f71453 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Javaslatok betöltése…"</string> <string name="controls_media_title" msgid="1746947284862928133">"Média"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Jelenlegi munkamenet elrejtése."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Elvetés"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Folytatás"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Beállítások"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Inaktív, ellenőrizze az appot"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index e6b71395c42a..4cc406bf9af7 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Բեռնման խորհուրդներ"</string> <string name="controls_media_title" msgid="1746947284862928133">"Մեդիա"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Թաքցրեք ընթացիկ աշխատաշրջանը"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Փակել"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Շարունակել"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Կարգավորումներ"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Ակտիվ չէ, ստուգեք հավելվածը"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index fec4205ba7da..5f7dce43925a 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Memuat rekomendasi"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Menyembunyikan sesi saat ini."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Tutup"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Lanjutkan"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Setelan"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Nonaktif, periksa aplikasi"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 3a9e63ba9f1a..b5f6e57002df 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Hleður tillögum"</string> <string name="controls_media_title" msgid="1746947284862928133">"Margmiðlunarefni"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Fela núverandi lotu."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Hunsa"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Halda áfram"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Stillingar"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Óvirkt, athugaðu forrit"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 3eca501b103c..f4453b76bb61 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Caricamento dei consigli"</string> <string name="controls_media_title" msgid="1746947284862928133">"Contenuti multimediali"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Nascondi la sessione attuale."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignora"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Riprendi"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Impostazioni"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Inattivo, controlla l\'app"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index e88c951cf19e..161fedc7a41e 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -1081,8 +1081,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"בטעינת המלצות"</string> <string name="controls_media_title" msgid="1746947284862928133">"מדיה"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"הסתרת הסשן הנוכחי."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"סגירה"</string> <string name="controls_media_resume" msgid="1933520684481586053">"המשך"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"הגדרות"</string> <string name="controls_error_timeout" msgid="794197289772728958">"לא פעיל, יש לבדוק את האפליקציה"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 130f682daaf3..fa9c040b29a9 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"候補を読み込んでいます"</string> <string name="controls_media_title" msgid="1746947284862928133">"メディア"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"現在のセッションを非表示にします。"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"閉じる"</string> <string name="controls_media_resume" msgid="1933520684481586053">"再開"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string> <string name="controls_error_timeout" msgid="794197289772728958">"無効: アプリをご確認ください"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index d050875c35ce..a3ddf70b5b25 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"მიმდინარეობს რეკომენდაციების ჩატვირთვა"</string> <string name="controls_media_title" msgid="1746947284862928133">"მედია"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"დაიმალოს მიმდინარე სესია"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"დახურვა"</string> <string name="controls_media_resume" msgid="1933520684481586053">"გაგრძელება"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"პარამეტრები"</string> <string name="controls_error_timeout" msgid="794197289772728958">"არააქტიურია, გადაამოწმეთ აპი"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 36c726edb791..0d3f7f9f1973 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Жүктеуге қатысты ұсыныстар"</string> <string name="controls_media_title" msgid="1746947284862928133">"Мультимедиа"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Ағымдағы сеансты жасыру"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Жабу"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Жалғастыру"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Параметрлер"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Өшірулі. Қолданба тексеріңіз."</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 9d87c583c007..5abd74000af5 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"កំពុងផ្ទុកការណែនាំ"</string> <string name="controls_media_title" msgid="1746947284862928133">"មេឌៀ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"លាក់វគ្គបច្ចុប្បន្ន។"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ច្រានចោល"</string> <string name="controls_media_resume" msgid="1933520684481586053">"បន្ត"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ការកំណត់"</string> <string name="controls_error_timeout" msgid="794197289772728958">"អសកម្ម ពិនិត្យមើលកម្មវិធី"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 901f024c5f71..47f23856c7b0 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -125,7 +125,7 @@ <string name="accessibility_accessibility_button" msgid="4089042473497107709">"ಪ್ರವೇಶಿಸುವಿಕೆ"</string> <string name="accessibility_rotate_button" msgid="1238584767612362586">"ಪರದೆಯನ್ನು ತಿರುಗಿಸಿ"</string> <string name="accessibility_recent" msgid="901641734769533575">"ಸಮಗ್ರ ನೋಟ"</string> - <string name="accessibility_search_light" msgid="524741790416076988">"ಹುಡುಕಿ"</string> + <string name="accessibility_search_light" msgid="524741790416076988">"Search"</string> <string name="accessibility_camera_button" msgid="2938898391716647247">"ಕ್ಯಾಮರಾ"</string> <string name="accessibility_phone_button" msgid="4256353121703100427">"ಫೋನ್"</string> <string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ಧ್ವನಿ ಸಹಾಯಕ"</string> @@ -441,7 +441,7 @@ <string name="expanded_header_battery_charging_with_time" msgid="757991461445765011">"<xliff:g id="CHARGING_TIME">%s</xliff:g> ಪೂರ್ಣಗೊಳ್ಳುವವರೆಗೆ"</string> <string name="expanded_header_battery_not_charging" msgid="809409140358955848">"ಚಾರ್ಜ್ ಆಗುತ್ತಿಲ್ಲ"</string> <string name="ssl_ca_cert_warning" msgid="8373011375250324005">"ನೆಟ್ವರ್ಕ್\n ವೀಕ್ಷಿಸಬಹುದಾಗಿರುತ್ತದೆ"</string> - <string name="description_target_search" msgid="3875069993128855865">"ಹುಡುಕಿ"</string> + <string name="description_target_search" msgid="3875069993128855865">"Search"</string> <string name="description_direction_up" msgid="3632251507574121434">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ಗಾಗಿ ಮೇಲಕ್ಕೆ ಸ್ಲೈಡ್ ಮಾಡಿ."</string> <string name="description_direction_left" msgid="4762708739096907741">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ಗಾಗಿ ಎಡಕ್ಕೆ ಸ್ಲೈಡ್ ಮಾಡಿ."</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"ಅಲಾರಾಂಗಳು, ಜ್ಞಾಪನೆಗಳು, ಈವೆಂಟ್ಗಳು ಹಾಗೂ ನೀವು ಸೂಚಿಸಿರುವ ಕರೆದಾರರನ್ನು ಹೊರತುಪಡಿಸಿ ಬೇರಾವುದೇ ಸದ್ದುಗಳು ಅಥವಾ ವೈಬ್ರೇಶನ್ಗಳು ನಿಮಗೆ ತೊಂದರೆ ನೀಡುವುದಿಲ್ಲ. ಹಾಗಿದ್ದರೂ, ನೀವು ಪ್ಲೇ ಮಾಡುವ ಸಂಗೀತ, ವೀಡಿಯೊಗಳು ಮತ್ತು ಆಟಗಳ ಆಡಿಯೊವನ್ನು ನೀವು ಕೇಳಿಸಿಕೊಳ್ಳುತ್ತೀರಿ."</string> @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ಶಿಫಾರಸುಗಳು ಲೋಡ್ ಆಗುತ್ತಿವೆ"</string> <string name="controls_media_title" msgid="1746947284862928133">"ಮಾಧ್ಯಮ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"ಪ್ರಸ್ತುತ ಸೆಶನ್ ಅನ್ನು ಮರೆಮಾಡಿ."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ವಜಾಗೊಳಿಸಿ"</string> <string name="controls_media_resume" msgid="1933520684481586053">"ಪುನರಾರಂಭಿಸಿ"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> <string name="controls_error_timeout" msgid="794197289772728958">"ನಿಷ್ಕ್ರಿಯ, ಆ್ಯಪ್ ಪರಿಶೀಲಿಸಿ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 8a6415566676..beb7edade8e6 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -502,10 +502,10 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"절전 모드 사용 중"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"성능 및 백그라운드 데이터를 줄입니다."</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"절전 모드 사용 중지"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>이(가) 녹화 또는 전송 중에 화면에 표시되거나 기기에서 재생되는 모든 정보에 액세스할 수 있습니다. 여기에는 비밀번호, 결제 세부정보, 사진, 메시지, 재생하는 오디오 같은 정보가 포함됩니다."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>이 녹화 또는 전송 중에 화면에 표시되거나 기기에서 재생되는 모든 정보에 액세스할 수 있습니다. 여기에는 비밀번호, 결제 세부정보, 사진, 메시지, 재생하는 오디오 같은 정보가 포함됩니다."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"이 기능을 제공하는 서비스는 녹화 또는 전송 중에 화면에 표시되거나 기기에서 재생되는 모든 정보에 액세스할 수 있습니다. 여기에는 비밀번호, 결제 세부정보, 사진, 메시지, 재생하는 오디오 같은 정보가 포함됩니다."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"녹화 또는 전송을 시작하시겠습니까?"</string> - <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>(으)로 녹화 또는 전송을 시작하시겠습니까?"</string> + <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>으로 녹화 또는 전송을 시작하시겠습니까?"</string> <string name="media_projection_remember_text" msgid="6896767327140422951">"다시 표시 안함"</string> <string name="clear_all_notifications_text" msgid="348312370303046130">"모두 지우기"</string> <string name="manage_notifications_text" msgid="6885645344647733116">"관리"</string> @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"추천 제어 기능 로드 중"</string> <string name="controls_media_title" msgid="1746947284862928133">"미디어"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"현재 세션을 숨깁니다."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"닫기"</string> <string name="controls_media_resume" msgid="1933520684481586053">"다시 시작"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"설정"</string> <string name="controls_error_timeout" msgid="794197289772728958">"비활성. 앱을 확인하세요."</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 3cf41264a4d4..4fbb09e56bbd 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Сунуштар жүктөлүүдө"</string> <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Учурдагы сеансты жашыруу."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Жабуу"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Улантуу"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Жөндөөлөр"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Жигерсиз. Колдонмону текшериңиз"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index b1c5a5cd887e..6d7feabfed1a 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ກຳລັງໂຫຼດຄຳແນະນຳ"</string> <string name="controls_media_title" msgid="1746947284862928133">"ມີເດຍ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"ເຊື່ອງເຊດຊັນປັດຈຸບັນ."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ປິດໄວ້"</string> <string name="controls_media_resume" msgid="1933520684481586053">"ສືບຕໍ່"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ການຕັ້ງຄ່າ"</string> <string name="controls_error_timeout" msgid="794197289772728958">"ບໍ່ເຮັດວຽກ, ກະລຸນາກວດສອບແອັບ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 4e9b802f4916..21217a06179f 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -1081,8 +1081,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Įkeliamos rekomendacijos"</string> <string name="controls_media_title" msgid="1746947284862928133">"Medija"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Slėpti dabartinį seansą."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Atsisakyti"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Tęsti"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Nustatymai"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Neaktyvu, patikrinkite progr."</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index a4c4666fafe3..4945dfa3f3db 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -1075,8 +1075,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Notiek ieteikumu ielāde"</string> <string name="controls_media_title" msgid="1746947284862928133">"Multivide"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Paslēpiet pašreizējo sesiju."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Nerādīt"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Atsākt"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Iestatījumi"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Neaktīva, pārbaudiet lietotni"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 7e623ad5032f..40d4698f7c21 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Се вчитуваат препораки"</string> <string name="controls_media_title" msgid="1746947284862928133">"Аудиовизуелни содржини"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Сокриј ја тековнава сесија."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Отфрли"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Продолжи"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Поставки"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Неактивна, провери апликација"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 35e642769981..c3bd65ef8e50 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -125,7 +125,7 @@ <string name="accessibility_accessibility_button" msgid="4089042473497107709">"ഉപയോഗസഹായി"</string> <string name="accessibility_rotate_button" msgid="1238584767612362586">"സ്ക്രീൻ തിരിക്കുക"</string> <string name="accessibility_recent" msgid="901641734769533575">"അവലോകനം"</string> - <string name="accessibility_search_light" msgid="524741790416076988">"തിരയൽ"</string> + <string name="accessibility_search_light" msgid="524741790416076988">"Search"</string> <string name="accessibility_camera_button" msgid="2938898391716647247">"ക്യാമറ"</string> <string name="accessibility_phone_button" msgid="4256353121703100427">"ഫോണ്"</string> <string name="accessibility_voice_assist_button" msgid="6497706615649754510">"വോയ്സ് സഹായം"</string> @@ -441,7 +441,7 @@ <string name="expanded_header_battery_charging_with_time" msgid="757991461445765011">"ഫുൾ ചാർജാകാൻ, <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string> <string name="expanded_header_battery_not_charging" msgid="809409140358955848">"ചാർജ്ജുചെയ്യുന്നില്ല"</string> <string name="ssl_ca_cert_warning" msgid="8373011375250324005">"നെറ്റ്വർക്ക്\nനിരീക്ഷിക്കപ്പെടാം"</string> - <string name="description_target_search" msgid="3875069993128855865">"തിരയൽ"</string> + <string name="description_target_search" msgid="3875069993128855865">"Search"</string> <string name="description_direction_up" msgid="3632251507574121434">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> എന്നതിനായി മുകളിലേയ്ക്ക് സ്ലൈഡുചെയ്യുക."</string> <string name="description_direction_left" msgid="4762708739096907741">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> എന്നതിനായി ഇടത്തേയ്ക്ക് സ്ലൈഡുചെയ്യുക."</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"നിങ്ങൾ സജ്ജീകരിച്ച അലാറങ്ങൾ, റിമൈൻഡറുകൾ, ഇവന്റുകൾ, കോളർമാർ എന്നിവയിൽ നിന്നുള്ള ശബ്ദങ്ങളും വൈബ്രേഷനുകളുമൊഴികെ മറ്റൊന്നും നിങ്ങളെ ശല്യപ്പെടുത്തുകയില്ല. സംഗീതം, വീഡിയോകൾ, ഗെയിമുകൾ എന്നിവയുൾപ്പെടെ പ്ലേ ചെയ്യുന്നതെന്തും നിങ്ങൾക്ക് തുടർന്നും കേൾക്കാൻ കഴിയും."</string> @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"നിർദ്ദേശങ്ങൾ ലോഡ് ചെയ്യുന്നു"</string> <string name="controls_media_title" msgid="1746947284862928133">"മീഡിയ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"നിലവിലെ സെഷൻ മറയ്ക്കുക."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ഡിസ്മിസ് ചെയ്യുക"</string> <string name="controls_media_resume" msgid="1933520684481586053">"പുനരാരംഭിക്കുക"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ക്രമീകരണം"</string> <string name="controls_error_timeout" msgid="794197289772728958">"നിഷ്ക്രിയം, ആപ്പ് പരിശോധിക്കൂ"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index b4690d418480..5f08f05ed9be 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Зөвлөмжүүдийг ачаалж байна"</string> <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Одоогийн харилцан үйлдлийг нуугаарай."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Хаах"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Үргэлжлүүлэх"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Тохиргоо"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Идэвхгүй байна, аппыг шалгана уу"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 4ea965ad80fc..6d525df2d05b 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -125,7 +125,7 @@ <string name="accessibility_accessibility_button" msgid="4089042473497107709">"अॅक्सेसिबिलिटी"</string> <string name="accessibility_rotate_button" msgid="1238584767612362586">"स्क्रीन फिरवा"</string> <string name="accessibility_recent" msgid="901641734769533575">"अवलोकन"</string> - <string name="accessibility_search_light" msgid="524741790416076988">"शोधा"</string> + <string name="accessibility_search_light" msgid="524741790416076988">"Search"</string> <string name="accessibility_camera_button" msgid="2938898391716647247">"कॅमेरा"</string> <string name="accessibility_phone_button" msgid="4256353121703100427">"फोन"</string> <string name="accessibility_voice_assist_button" msgid="6497706615649754510">"व्हॉइस सहाय्य"</string> @@ -441,7 +441,7 @@ <string name="expanded_header_battery_charging_with_time" msgid="757991461445765011">"<xliff:g id="CHARGING_TIME">%s</xliff:g> पूर्ण होईपर्यंत"</string> <string name="expanded_header_battery_not_charging" msgid="809409140358955848">"चार्ज होत नाही"</string> <string name="ssl_ca_cert_warning" msgid="8373011375250324005">"नेटवर्कचे परीक्षण\nकेले जाऊ शकते"</string> - <string name="description_target_search" msgid="3875069993128855865">"शोध"</string> + <string name="description_target_search" msgid="3875069993128855865">"Search"</string> <string name="description_direction_up" msgid="3632251507574121434">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> साठी वर स्लाइड करा."</string> <string name="description_direction_left" msgid="4762708739096907741">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> साठी डावीकडे स्लाइड करा."</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"अलार्म, रिमाइंडर, इव्हेंट आणि तुम्ही निश्चित केलेल्या कॉलर व्यतिरिक्त तुम्हाला कोणत्याही आवाज आणि कंपनांचा व्यत्त्यय आणला जाणार नाही. तरीही तुम्ही प्ले करायचे ठरवलेले कोणतेही संगीत, व्हिडिओ आणि गेमचे आवाज ऐकू शकतात."</string> @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"शिफारशी लोड करत आहे"</string> <string name="controls_media_title" msgid="1746947284862928133">"मीडिया"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"सध्याचे सेशन लपवा."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"डिसमिस करा"</string> <string name="controls_media_resume" msgid="1933520684481586053">"पुन्हा सुरू करा"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग्ज"</string> <string name="controls_error_timeout" msgid="794197289772728958">"निष्क्रिय, ॲप तपासा"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 38ee25c7f3e4..4f832e4c450d 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Memuatkan cadangan"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Sembunyikan sesi semasa."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Tolak"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Sambung semula"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Tetapan"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Tidak aktif, semak apl"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index d872a89880c9..849c2e9e7381 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Laster inn anbefalinger"</string> <string name="controls_media_title" msgid="1746947284862928133">"Medier"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Skjul den nåværende økten."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Lukk"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Gjenoppta"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Innstillinger"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv. Sjekk appen"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 1974e8088971..2ac442ec0225 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"सिफारिसहरू लोड गर्दै"</string> <string name="controls_media_title" msgid="1746947284862928133">"मिडिया"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"हालको सत्र लुकाउनुहोस्।"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"हटाउनुहोस्"</string> <string name="controls_media_resume" msgid="1933520684481586053">"सुचारु गर्नुहोस्"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिङ"</string> <string name="controls_error_timeout" msgid="794197289772728958">"निष्क्रिय छ, एप जाँच गर्नु…"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 9da8afa4d698..da0f42754acd 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Aanbevelingen laden"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"De huidige sessie verbergen."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Sluiten"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Hervatten"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Instellingen"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Inactief, check de app"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 5b5cbc62fe21..029aa69b6fbf 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -125,7 +125,7 @@ <string name="accessibility_accessibility_button" msgid="4089042473497107709">"ଆକ୍ସେସିବିଲିଟୀ"</string> <string name="accessibility_rotate_button" msgid="1238584767612362586">"ସ୍କ୍ରୀନ୍କୁ ଘୁରାନ୍ତୁ"</string> <string name="accessibility_recent" msgid="901641734769533575">"ଓଭରଭିଉ"</string> - <string name="accessibility_search_light" msgid="524741790416076988">"ସର୍ଚ୍ଚ କରନ୍ତୁ"</string> + <string name="accessibility_search_light" msgid="524741790416076988">"Search"</string> <string name="accessibility_camera_button" msgid="2938898391716647247">"କ୍ୟାମେରା"</string> <string name="accessibility_phone_button" msgid="4256353121703100427">"ଫୋନ୍"</string> <string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ଭଏସ୍ ସହାୟକ"</string> @@ -441,7 +441,7 @@ <string name="expanded_header_battery_charging_with_time" msgid="757991461445765011">"ପୂର୍ଣ୍ଣ ଚାର୍ଜ ହେବାକୁ ଆଉ <xliff:g id="CHARGING_TIME">%s</xliff:g> ଅଛି"</string> <string name="expanded_header_battery_not_charging" msgid="809409140358955848">"ଚାର୍ଜ ହେଉନାହିଁ"</string> <string name="ssl_ca_cert_warning" msgid="8373011375250324005">"ନେଟ୍ୱର୍କ\nମନିଟର୍ କରାଯାଇପାରେ"</string> - <string name="description_target_search" msgid="3875069993128855865">"ସର୍ଚ୍ଚ"</string> + <string name="description_target_search" msgid="3875069993128855865">"Search"</string> <string name="description_direction_up" msgid="3632251507574121434">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ପାଇଁ ଉପରକୁ ସ୍ଲାଇଡ୍ କରନ୍ତୁ।"</string> <string name="description_direction_left" msgid="4762708739096907741">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ପାଇଁ ବାମକୁ ସ୍ଲାଇଡ୍ କରନ୍ତୁ"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"ଆଲାର୍ମ, ରିମାଇଣ୍ଡର୍, ଇଭେଣ୍ଟ ଏବଂ ଆପଣ ନିର୍ଦ୍ଦିଷ୍ଟ କରିଥିବା କଲର୍ଙ୍କ ବ୍ୟତୀତ ଆପଣଙ୍କ ଧ୍ୟାନ ଅନ୍ୟ କୌଣସି ଧ୍ୱନୀ ଏବଂ ଭାଇବ୍ରେଶନ୍ରେ ଆକର୍ଷଣ କରାଯିବନାହିଁ। ମ୍ୟୁଜିକ୍, ଭିଡିଓ ଏବଂ ଗେମ୍ ସମେତ ନିଜେ ଚଲାଇବାକୁ ବାଛିଥିବା ଅନ୍ୟ ସବୁକିଛି ଆପଣ ଶୁଣିପାରିବେ।"</string> @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ସୁପାରିଶଗୁଡ଼ିକ ଲୋଡ୍ କରାଯାଉଛି"</string> <string name="controls_media_title" msgid="1746947284862928133">"ମିଡିଆ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"ବର୍ତ୍ତମାନର ସେସନ୍ ଲୁଚାନ୍ତୁ।"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ଖାରଜ କରନ୍ତୁ"</string> <string name="controls_media_resume" msgid="1933520684481586053">"ପୁଣି ଆରମ୍ଭ କରନ୍ତୁ"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ସେଟିଂସ୍"</string> <string name="controls_error_timeout" msgid="794197289772728958">"ନିଷ୍କ୍ରିୟ ଅଛି, ଆପ ଯାଞ୍ଚ କରନ୍ତୁ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 5c31ce71ef98..ed5f40caf7d0 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ਸਿਫ਼ਾਰਸ਼ਾਂ ਲੋਡ ਹੋ ਰਹੀਆਂ ਹਨ"</string> <string name="controls_media_title" msgid="1746947284862928133">"ਮੀਡੀਆ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਨੂੰ ਲੁਕਾਓ।"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ਖਾਰਜ ਕਰੋ"</string> <string name="controls_media_resume" msgid="1933520684481586053">"ਮੁੜ-ਚਾਲੂ ਕਰੋ"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ਸੈਟਿੰਗਾਂ"</string> <string name="controls_error_timeout" msgid="794197289772728958">"ਅਕਿਰਿਆਸ਼ੀਲ, ਐਪ ਦੀ ਜਾਂਚ ਕਰੋ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index b7ee75057626..ba30cedd9e39 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -1081,8 +1081,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Wczytuję rekomendacje"</string> <string name="controls_media_title" msgid="1746947284862928133">"Multimedia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Ukryj bieżącą sesję."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odrzuć"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Wznów"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Ustawienia"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Nieaktywny, sprawdź aplikację"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 49c3ba0c2cd8..cf428a39d4a7 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -1075,8 +1075,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Se încarcă recomandările"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Ascunde sesiunea actuală."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Închideți"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Reia"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Setări"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Inactiv, verificați aplicația"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 7fb456d5f425..ba86e9501006 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -1081,8 +1081,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Загрузка рекомендаций…"</string> <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Скрыть текущий сеанс?"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Скрыть"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Возобновить"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Нет ответа. Проверьте приложение."</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 9d7e7d1cd27c..75486073fd5e 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -1081,8 +1081,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Načítavajú sa odporúčania"</string> <string name="controls_media_title" msgid="1746947284862928133">"Médiá"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Skryť aktuálnu reláciu."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Zavrieť"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Pokračovať"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavenia"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Neaktívne, preverte aplikáciu"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index ecbf1d905e5b..07ad8c1ca99e 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -1081,8 +1081,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Nalaganje priporočil"</string> <string name="controls_media_title" msgid="1746947284862928133">"Predstavnost"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Skrije trenutno sejo."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Opusti"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Nadaljuj"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavitve"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, poglejte aplikacijo"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 70245b875726..b92744d7f087 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Po ngarkon rekomandimet"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Fshih sesionin aktual."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Hiq"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Vazhdo"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Cilësimet"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Joaktive, kontrollo aplikacionin"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 1d02ca6ab455..f57d92bb911e 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -1075,8 +1075,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Учитавају се препоруке"</string> <string name="controls_media_title" msgid="1746947284862928133">"Медији"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Сакријте актуелну сесију."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Одбаци"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Настави"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Подешавања"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Неактивно. Видите апликацију"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 7a7448be16c5..876e3377171c 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Rekommendationer läses in"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Dölj den aktuella sessionen."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Stäng"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Återuppta"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Inställningar"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv, kolla appen"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index b8d95fcd0c1a..25e349367b52 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Inapakia mapendekezo"</string> <string name="controls_media_title" msgid="1746947284862928133">"Maudhui"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Ficha kipindi cha sasa."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ondoa"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Endelea"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Mipangilio"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Haitumiki, angalia programu"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 60c1cf9dcac0..19e67db62de8 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"பரிந்துரைகளை ஏற்றுகிறது"</string> <string name="controls_media_title" msgid="1746947284862928133">"மீடியா"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"இந்த அமர்வை மறையுங்கள்."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"மூடுக"</string> <string name="controls_media_resume" msgid="1933520684481586053">"தொடர்க"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"அமைப்புகள்"</string> <string name="controls_error_timeout" msgid="794197289772728958">"செயலில் இல்லை , சரிபார்க்கவும்"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 6cfe03d95023..34ff9b102da0 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -125,7 +125,7 @@ <string name="accessibility_accessibility_button" msgid="4089042473497107709">"యాక్సెస్ సామర్థ్యం"</string> <string name="accessibility_rotate_button" msgid="1238584767612362586">"స్క్రీన్ను తిప్పండి"</string> <string name="accessibility_recent" msgid="901641734769533575">"ఓవర్వ్యూ"</string> - <string name="accessibility_search_light" msgid="524741790416076988">"వెతుకు"</string> + <string name="accessibility_search_light" msgid="524741790416076988">"సెర్చ్"</string> <string name="accessibility_camera_button" msgid="2938898391716647247">"కెమెరా"</string> <string name="accessibility_phone_button" msgid="4256353121703100427">"ఫోన్"</string> <string name="accessibility_voice_assist_button" msgid="6497706615649754510">"వాయిస్ అసిస్టెంట్"</string> @@ -441,7 +441,7 @@ <string name="expanded_header_battery_charging_with_time" msgid="757991461445765011">"పూర్తిగా నిండటానికి <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string> <string name="expanded_header_battery_not_charging" msgid="809409140358955848">"ఛార్జ్ కావడం లేదు"</string> <string name="ssl_ca_cert_warning" msgid="8373011375250324005">"నెట్వర్క్\nపర్యవేక్షించబడవచ్చు"</string> - <string name="description_target_search" msgid="3875069993128855865">"శోధించండి"</string> + <string name="description_target_search" msgid="3875069993128855865">"సెర్చ్"</string> <string name="description_direction_up" msgid="3632251507574121434">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> కోసం పైకి స్లైడ్ చేయండి."</string> <string name="description_direction_left" msgid="4762708739096907741">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> కోసం ఎడమవైపుకు స్లైడ్ చేయండి."</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"మీరు పేర్కొనే అలారాలు, రిమైండర్లు, ఈవెంట్లు మరియు కాలర్ల నుండి మినహా మరే ఇతర ధ్వనులు మరియు వైబ్రేషన్లతో మీకు అంతరాయం కలగదు. మీరు ఇప్పటికీ సంగీతం, వీడియోలు మరియు గేమ్లతో సహా మీరు ప్లే చేయడానికి ఎంచుకున్నవి ఏవైనా వింటారు."</string> @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"సిఫార్సులు లోడ్ అవుతున్నాయి"</string> <string name="controls_media_title" msgid="1746947284862928133">"మీడియా"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"ప్రస్తుత సెషన్ను దాచు."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"విస్మరించు"</string> <string name="controls_media_resume" msgid="1933520684481586053">"కొనసాగించండి"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"సెట్టింగ్లు"</string> <string name="controls_error_timeout" msgid="794197289772728958">"ఇన్యాక్టివ్, యాప్ చెక్ చేయండి"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index a2c1127df5ca..d37be57e0136 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"กำลังโหลดคำแนะนำ"</string> <string name="controls_media_title" msgid="1746947284862928133">"สื่อ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"ซ่อนเซสชันปัจจุบัน"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ปิด"</string> <string name="controls_media_resume" msgid="1933520684481586053">"เล่นต่อ"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"การตั้งค่า"</string> <string name="controls_error_timeout" msgid="794197289772728958">"ไม่มีการใช้งาน โปรดตรวจสอบแอป"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index e5f0532b0e7b..af474c761fc5 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Nilo-load ang rekomendasyon"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Itago ang kasalukuyang session."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"I-dismiss"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Ituloy"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Mga Setting"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Hindi aktibo, tingnan ang app"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 4943e2d03920..ee494a25e366 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Öneriler yükleniyor"</string> <string name="controls_media_title" msgid="1746947284862928133">"Medya"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Mevcut oturumu gizle."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Kapat"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Devam ettir"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Devre dışı, uygulamaya bakın"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 447912e08835..c444c47d1441 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -1081,8 +1081,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Завантаження рекомендацій"</string> <string name="controls_media_title" msgid="1746947284862928133">"Медіа"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Приховати поточний сеанс."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Закрити"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Відновити"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Налаштування"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Неактивно, перейдіть у додаток"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 20b551882344..729ed5a3ac51 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"تجاویز لوڈ ہو رہی ہیں"</string> <string name="controls_media_title" msgid="1746947284862928133">"میڈیا"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"موجودہ سیشن چھپائیں۔"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"برخاست کریں"</string> <string name="controls_media_resume" msgid="1933520684481586053">"دوبارہ شروع کریں"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ترتیبات"</string> <string name="controls_error_timeout" msgid="794197289772728958">"غیر فعال، ایپ چیک کریں"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index a12a08d0a1e6..5794bdf3c8eb 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Đang tải các đề xuất"</string> <string name="controls_media_title" msgid="1746947284862928133">"Nội dung nghe nhìn"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Ẩn phiên hiện tại."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Đóng"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Tiếp tục"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Cài đặt"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Không hoạt động, hãy kiểm tra ứng dụng"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 3382365b7ccb..41c132c9faf6 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在加载推荐内容"</string> <string name="controls_media_title" msgid="1746947284862928133">"媒体"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"隐藏当前会话。"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"关闭"</string> <string name="controls_media_resume" msgid="1933520684481586053">"继续播放"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"设置"</string> <string name="controls_error_timeout" msgid="794197289772728958">"无效,请检查应用"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 1d55ca22a43f..b2e1b90de007 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在載入建議"</string> <string name="controls_media_title" msgid="1746947284862928133">"媒體"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"隱藏目前的工作階段。"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"關閉"</string> <string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string> <string name="controls_error_timeout" msgid="794197289772728958">"已停用,請檢查應用程式"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index e62c164684b3..346b1239cf0f 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在載入建議控制項"</string> <string name="controls_media_title" msgid="1746947284862928133">"媒體"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"隱藏目前的工作階段。"</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"關閉"</string> <string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string> <string name="controls_error_timeout" msgid="794197289772728958">"無效,請查看應用程式"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index bd7391203dd8..5abce7fb4415 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -1069,8 +1069,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Ilayisha izincomo"</string> <string name="controls_media_title" msgid="1746947284862928133">"Imidiya"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Fihla iseshini yamanje."</string> - <!-- no translation found for controls_media_dismiss_button (9081375542265132213) --> - <skip /> + <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Cashisa"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Qalisa kabusha"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Izilungiselelo"</string> <string name="controls_error_timeout" msgid="794197289772728958">"Akusebenzi, hlola uhlelo lokusebenza"</string> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index 84dbd60b799d..a62502965dd2 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -159,7 +159,7 @@ <declare-styleable name="UdfpsView"> <attr name="sensorRadius" format="dimension"/> - <attr name="sensorMarginBottom" format="dimension"/> + <attr name="sensorCenterY" format="dimension"/> <attr name="sensorPressureCoefficient" format="float"/> <attr name="sensorTouchAreaCoefficient" format="float"/> </declare-styleable> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 17ba7c99dc0c..ce1ca5ad1723 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -565,15 +565,9 @@ <!-- If the config font scale is >= this value, potentially adjust the number of columns--> <item name="controls_max_columns_adjust_above_font_scale" translatable="false" format="float" type="dimen">1.25</item> - <!-- One handed mode default offset % of display size --> - <fraction name="config_one_handed_offset">50%</fraction> - <!-- Allow one handed to enable round corner --> <bool name="config_one_handed_enable_round_corner">true</bool> - <!-- Animation duration for translating of one handed when trigger / dismiss. --> - <integer name="config_one_handed_translate_animation_duration">150</integer> - <!-- Show a separate icon for low and high volume on the volume dialog --> <bool name="config_showLowMediaVolumeIcon">false</bool> </resources> diff --git a/packages/SystemUI/res/values/config_tv.xml b/packages/SystemUI/res/values/config_tv.xml index 5cb840f24c84..7451ba8e88a4 100644 --- a/packages/SystemUI/res/values/config_tv.xml +++ b/packages/SystemUI/res/values/config_tv.xml @@ -22,9 +22,4 @@ <!-- Whether to enable microphone disclosure indicator (com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar). --> <bool name="audio_recording_disclosure_enabled">true</bool> - - <!-- Whether the indicator should expand and show the recording application's label. - When disabled (false) the "minimized" indicator would appear on the screen whenever an - application is recording, but will not reveal to the user what application this is. --> - <bool name="audio_recording_disclosure_reveal_packages">false</bool> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 122fcb21a9f4..765a9422585a 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1036,6 +1036,11 @@ <!-- The maximum offset in either direction that icons move to prevent burn-in on AOD. --> <dimen name="default_burn_in_prevention_offset">15dp</dimen> + <!-- The maximum offset for the under-display fingerprint sensor (UDFPS) icon in either + direction that elements aer moved to prevent burn-in on AOD--> + <dimen name="udfps_burn_in_offset_x">8dp</dimen> + <dimen name="udfps_burn_in_offset_y">8dp</dimen> + <dimen name="corner_size">8dp</dimen> <dimen name="top_padding">0dp</dimen> <dimen name="bottom_padding">48dp</dimen> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextUtils.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextUtils.java new file mode 100644 index 000000000000..1de740a083c2 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextUtils.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.shared.system; + +import android.annotation.UserIdInt; +import android.content.Context; + +public class ContextUtils { + + /** Get the user associated with this context */ + public static @UserIdInt int getUserId(Context context) { + return context.getUserId(); + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index d6fabd63420d..6f19613be28f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -36,8 +36,8 @@ import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.statusbar.NavigationBarController; -import com.android.systemui.statusbar.phone.NavigationBarView; +import com.android.systemui.navigationbar.NavigationBarController; +import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.util.InjectionInflationController; public class KeyguardDisplayManager { @@ -54,9 +54,6 @@ public class KeyguardDisplayManager { private final SparseArray<Presentation> mPresentations = new SparseArray<>(); - private final NavigationBarController mNavBarController = - Dependency.get(NavigationBarController.class); - private final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() { @@ -227,7 +224,8 @@ public class KeyguardDisplayManager { // Leave this task to {@link StatusBarKeyguardViewManager} if (displayId == DEFAULT_DISPLAY) return; - NavigationBarView navBarView = mNavBarController.getNavigationBarView(displayId); + NavigationBarView navBarView = Dependency.get(NavigationBarController.class) + .getNavigationBarView(displayId); // We may not have nav bar on a display. if (navBarView == null) return; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 1db2e32b8cdb..b0483339d14e 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -256,7 +256,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y); mInjectionInflationController = new InjectionInflationController( - SystemUIFactory.getInstance().getRootComponent()); + SystemUIFactory.getInstance().getSysUIComponent().createViewInstanceCreatorFactory()); mViewConfiguration = ViewConfiguration.get(context); mKeyguardStateController = Dependency.get(KeyguardStateController.class); mSecondaryLockScreenController = new AdminSecondaryLockScreenController(context, this, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java index 17abfae3c7e1..ac2160ecb4ae 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java @@ -24,11 +24,11 @@ import android.telephony.TelephonyManager; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.Dependency; +import com.android.systemui.dagger.SysUISingleton; import javax.inject.Inject; -import javax.inject.Singleton; -@Singleton +@SysUISingleton public class KeyguardSecurityModel { /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 878947f6ba37..c354241da7b4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -94,6 +94,7 @@ import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; @@ -124,14 +125,13 @@ import java.util.concurrent.Executor; import java.util.function.Consumer; import javax.inject.Inject; -import javax.inject.Singleton; /** * Watches for updates that may be interesting to the keyguard, and provides * the up to date information as well as a registration for callbacks that care * to be updated. */ -@Singleton +@SysUISingleton public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpable { private static final String TAG = "KeyguardUpdateMonitor"; diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index 2200b22b8b27..3775628df0e0 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -34,6 +34,7 @@ import androidx.lifecycle.Observer; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManager.DockEventListener; import com.android.systemui.plugins.ClockPlugin; @@ -50,12 +51,11 @@ import java.util.Objects; import java.util.function.Supplier; import javax.inject.Inject; -import javax.inject.Singleton; /** * Manages custom clock faces for AOD and lock screen. */ -@Singleton +@SysUISingleton public final class ClockManager { private static final String TAG = "ClockOptsProvider"; diff --git a/packages/SystemUI/src/com/android/systemui/ActivityIntentHelper.java b/packages/SystemUI/src/com/android/systemui/ActivityIntentHelper.java index 63840bcb293a..43b3929808b3 100644 --- a/packages/SystemUI/src/com/android/systemui/ActivityIntentHelper.java +++ b/packages/SystemUI/src/com/android/systemui/ActivityIntentHelper.java @@ -22,15 +22,16 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import com.android.systemui.dagger.SysUISingleton; + import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * Contains useful methods for querying properties of an Activity Intent. */ -@Singleton +@SysUISingleton public class ActivityIntentHelper { private final Context mContext; diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java index 47a10af7c550..3d6d38149fc5 100644 --- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java @@ -18,13 +18,13 @@ import android.app.PendingIntent; import android.content.Intent; import android.view.View; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.phone.StatusBar; import java.util.Optional; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; @@ -33,7 +33,7 @@ import dagger.Lazy; * delegates to an actual implementation (StatusBar). */ @SuppressWarnings("OptionalUsedAsFieldOrParameterType") -@Singleton +@SysUISingleton public class ActivityStarterDelegate implements ActivityStarter { private Optional<Lazy<StatusBar>> mActualStarter; diff --git a/packages/SystemUI/src/com/android/systemui/BootCompleteCacheImpl.kt b/packages/SystemUI/src/com/android/systemui/BootCompleteCacheImpl.kt index aef1872f6520..9eaf4c96c896 100644 --- a/packages/SystemUI/src/com/android/systemui/BootCompleteCacheImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/BootCompleteCacheImpl.kt @@ -18,13 +18,13 @@ package com.android.systemui import android.util.Log import com.android.internal.annotations.GuardedBy +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import java.io.FileDescriptor import java.io.PrintWriter import java.lang.ref.WeakReference import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject -import javax.inject.Singleton /** * Caches whether the device has reached [SystemService.PHASE_BOOT_COMPLETED]. @@ -32,7 +32,7 @@ import javax.inject.Singleton * This class is constructed and set by [SystemUIApplication] and will notify all listeners when * boot is completed. */ -@Singleton +@SysUISingleton class BootCompleteCacheImpl @Inject constructor(dumpManager: DumpManager) : BootCompleteCache, Dumpable { diff --git a/packages/SystemUI/src/com/android/systemui/DemoMode.java b/packages/SystemUI/src/com/android/systemui/DemoMode.java deleted file mode 100644 index 5c3971571b87..000000000000 --- a/packages/SystemUI/src/com/android/systemui/DemoMode.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2013 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 com.android.systemui; - -import android.os.Bundle; - -public interface DemoMode { - - public static final String DEMO_MODE_ALLOWED = "sysui_demo_allowed"; - - void dispatchDemoCommand(String command, Bundle args); - - public static final String ACTION_DEMO = "com.android.systemui.demo"; - - public static final String EXTRA_COMMAND = "command"; - - public static final String COMMAND_ENTER = "enter"; - public static final String COMMAND_EXIT = "exit"; - public static final String COMMAND_CLOCK = "clock"; - public static final String COMMAND_BATTERY = "battery"; - public static final String COMMAND_NETWORK = "network"; - public static final String COMMAND_BARS = "bars"; - public static final String COMMAND_STATUS = "status"; - public static final String COMMAND_NOTIFICATIONS = "notifications"; - public static final String COMMAND_VOLUME = "volume"; - public static final String COMMAND_OPERATOR = "operator"; -} diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 58f8c07ad7c9..748a9c9448c5 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -39,6 +39,7 @@ import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; @@ -47,6 +48,8 @@ import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.NavigationBarController; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.PluginDependencyProvider; @@ -64,18 +67,18 @@ import com.android.systemui.shared.system.DevicePolicyManagerWrapper; import com.android.systemui.shared.system.PackageManagerWrapper; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.NavigationBarController; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment; import com.android.systemui.statusbar.notification.NotificationFilter; -import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -85,10 +88,8 @@ import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.ManagedProfileController; -import com.android.systemui.statusbar.phone.NavigationModeController; import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper; import com.android.systemui.statusbar.phone.NotificationGroupManager; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -131,7 +132,6 @@ import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Named; -import javax.inject.Singleton; import dagger.Lazy; @@ -150,7 +150,7 @@ import dagger.Lazy; * they have no clients they should not have any registered resources like bound * services, registered receivers, etc. */ -@Singleton +@SysUISingleton public class Dependency { /** * Key for getting a the main looper. diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java index 2deeb1230f09..d859a63d0943 100644 --- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java +++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java @@ -24,36 +24,26 @@ import android.util.SparseArray; import com.android.internal.messages.nano.SystemMessageProto; import com.android.systemui.appops.AppOpsController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.util.Assert; -import java.util.Set; - import javax.inject.Inject; -import javax.inject.Singleton; /** * Tracks state of foreground services and notifications related to foreground services per user. */ -@Singleton +@SysUISingleton public class ForegroundServiceController { - public static final int[] APP_OPS = new int[] {AppOpsManager.OP_CAMERA, - AppOpsManager.OP_SYSTEM_ALERT_WINDOW, - AppOpsManager.OP_RECORD_AUDIO, - AppOpsManager.OP_COARSE_LOCATION, - AppOpsManager.OP_FINE_LOCATION}; + public static final int[] APP_OPS = new int[] {AppOpsManager.OP_SYSTEM_ALERT_WINDOW}; private final SparseArray<ForegroundServicesUserState> mUserServices = new SparseArray<>(); private final Object mMutex = new Object(); - private final NotificationEntryManager mEntryManager; private final Handler mMainHandler; @Inject - public ForegroundServiceController(NotificationEntryManager entryManager, - AppOpsController appOpsController, @Main Handler mainHandler) { - mEntryManager = entryManager; + public ForegroundServiceController(AppOpsController appOpsController, + @Main Handler mainHandler) { mMainHandler = mainHandler; appOpsController.addCallback(APP_OPS, (code, uid, packageName, active) -> { mMainHandler.post(() -> { @@ -87,19 +77,6 @@ public class ForegroundServiceController { } /** - * Returns the keys for notifications from this package using the standard template, - * if they exist. - */ - @Nullable - public ArraySet<String> getStandardLayoutKeys(int userId, String pkg) { - synchronized (mMutex) { - final ForegroundServicesUserState services = mUserServices.get(userId); - if (services == null) return null; - return services.getStandardLayoutKeys(pkg); - } - } - - /** * Gets active app ops for this user and package */ @Nullable @@ -140,31 +117,6 @@ public class ForegroundServiceController { userServices.removeOp(packageName, appOpCode); } } - - // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by - // AppOpsCoordinator - // Update appOps if there are associated pending or visible notifications - final Set<String> notificationKeys = getStandardLayoutKeys(userId, packageName); - if (notificationKeys != null) { - boolean changed = false; - for (String key : notificationKeys) { - final NotificationEntry entry = mEntryManager.getPendingOrActiveNotif(key); - if (entry != null - && uid == entry.getSbn().getUid() - && packageName.equals(entry.getSbn().getPackageName())) { - synchronized (entry.mActiveAppOps) { - if (active) { - changed |= entry.mActiveAppOps.add(appOpCode); - } else { - changed |= entry.mActiveAppOps.remove(appOpCode); - } - } - } - } - if (changed) { - mEntryManager.updateNotifications("appOpChanged pkg=" + packageName); - } - } } /** diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java index bb445832da93..d6cb1142061d 100644 --- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java +++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java @@ -21,10 +21,10 @@ import android.app.NotificationManager; import android.content.Context; import android.os.Bundle; import android.service.notification.StatusBarNotification; -import android.util.ArraySet; import android.util.Log; import com.android.internal.statusbar.NotificationVisibility; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotifPipeline; @@ -33,10 +33,9 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.util.time.SystemClock; import javax.inject.Inject; -import javax.inject.Singleton; /** Updates foreground service notification state in response to notification data events. */ -@Singleton +@SysUISingleton public class ForegroundServiceNotificationListener { private static final String TAG = "FgServiceController"; @@ -172,24 +171,8 @@ public class ForegroundServiceNotificationListener { sbn.getPackageName(), sbn.getKey()); } } - tagAppOps(entry); return true; }, true /* create if not found */); } - - // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by - // AppOpsCoordinator - private void tagAppOps(NotificationEntry entry) { - final StatusBarNotification sbn = entry.getSbn(); - ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps( - sbn.getUserId(), - sbn.getPackageName()); - synchronized (entry.mActiveAppOps) { - entry.mActiveAppOps.clear(); - if (activeOps != null) { - entry.mActiveAppOps.addAll(activeOps); - } - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/InitController.java b/packages/SystemUI/src/com/android/systemui/InitController.java index a2dd12389fa9..9bb576b1f3dd 100644 --- a/packages/SystemUI/src/com/android/systemui/InitController.java +++ b/packages/SystemUI/src/com/android/systemui/InitController.java @@ -14,16 +14,17 @@ package com.android.systemui; +import com.android.systemui.dagger.SysUISingleton; + import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Singleton; /** * Created by {@link Dependency} on SystemUI startup. Add tasks which need to be executed only * after all other dependencies have been created. */ -@Singleton +@SysUISingleton public class InitController { /** diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java index dc0cb03e9b23..9c5a40c22bd8 100644 --- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java +++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java @@ -30,16 +30,16 @@ import android.os.SystemClock; import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.phone.BiometricUnlockController; import javax.inject.Inject; -import javax.inject.Singleton; /** * Class that only runs on debuggable builds that listens to broadcasts that simulate actions in the * system that are used for testing the latency. */ -@Singleton +@SysUISingleton public class LatencyTester extends SystemUI { private static final String diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index ad11d71eb132..f663d1a0d77c 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -83,6 +83,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.qs.SecureSetting; import com.android.systemui.tuner.TunerService; @@ -92,13 +93,12 @@ import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * An overlay that draws screen decorations in software (e.g for rounded corners or display cutout) * for antialiasing and emulation purposes. */ -@Singleton +@SysUISingleton public class ScreenDecorations extends SystemUI implements Tunable { private static final boolean DEBUG = false; private static final String TAG = "ScreenDecorations"; diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java index 73295a3d8814..34efa35c37c5 100644 --- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java @@ -41,6 +41,7 @@ import android.widget.LinearLayout; import android.widget.PopupWindow; import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.statusbar.CommandQueue; @@ -48,10 +49,9 @@ import com.android.systemui.statusbar.CommandQueue; import java.lang.ref.WeakReference; import javax.inject.Inject; -import javax.inject.Singleton; /** Shows a restart-activity button when the foreground activity is in size compatibility mode. */ -@Singleton +@SysUISingleton public class SizeCompatModeActivityController extends SystemUI implements CommandQueue.Callbacks { private static final String TAG = "SizeCompatMode"; diff --git a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java index 7a4ef2b7bbf2..0b997d0e0955 100644 --- a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java +++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java @@ -29,15 +29,15 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.SliceBroadcastRelay; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import javax.inject.Inject; -import javax.inject.Singleton; /** * Allows settings to register certain broadcasts to launch the settings app for pinned slices. * @see SliceBroadcastRelay */ -@Singleton +@SysUISingleton public class SliceBroadcastRelayHandler extends SystemUI { private static final String TAG = "SliceBroadcastRelay"; private static final boolean DEBUG = false; diff --git a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java index 449ed8c3bcdb..2365f128532e 100644 --- a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java @@ -16,6 +16,7 @@ package com.android.systemui; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; +import android.annotation.Nullable; import android.app.Activity; import android.app.AlertDialog; import android.app.slice.SliceManager; @@ -29,6 +30,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.net.Uri; import android.os.Bundle; import android.text.BidiFormatter; +import android.util.EventLog; import android.util.Log; import android.widget.CheckBox; import android.widget.TextView; @@ -50,10 +52,17 @@ public class SlicePermissionActivity extends Activity implements OnClickListener mUri = getIntent().getParcelableExtra(SliceProvider.EXTRA_BIND_URI); mCallingPkg = getIntent().getStringExtra(SliceProvider.EXTRA_PKG); - mProviderPkg = getIntent().getStringExtra(SliceProvider.EXTRA_PROVIDER_PKG); + if (mUri == null) { + Log.e(TAG, SliceProvider.EXTRA_BIND_URI + " wasn't provided"); + finish(); + return; + } try { PackageManager pm = getPackageManager(); + mProviderPkg = pm.resolveContentProvider(mUri.getAuthority(), + PackageManager.GET_META_DATA).applicationInfo.packageName; + verifyCallingPkg(); CharSequence app1 = BidiFormatter.getInstance().unicodeWrap(pm.getApplicationInfo( mCallingPkg, 0).loadSafeLabel(pm, PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX, PackageItemInfo.SAFE_LABEL_FLAG_TRIM @@ -97,4 +106,29 @@ public class SlicePermissionActivity extends Activity implements OnClickListener public void onDismiss(DialogInterface dialog) { finish(); } + + private void verifyCallingPkg() { + final String providerPkg = getIntent().getStringExtra("provider_pkg"); + if (providerPkg == null || mProviderPkg.equals(providerPkg)) return; + final String callingPkg = getCallingPkg(); + EventLog.writeEvent(0x534e4554, "159145361", getUid(callingPkg), String.format( + "pkg %s (disguised as %s) attempted to request permission to show %s slices in %s", + callingPkg, providerPkg, mProviderPkg, mCallingPkg)); + } + + @Nullable + private String getCallingPkg() { + final Uri referrer = getReferrer(); + if (referrer == null) return null; + return referrer.getHost(); + } + + private int getUid(@Nullable final String pkg) { + if (pkg == null) return -1; + try { + return getPackageManager().getApplicationInfo(pkg, 0).uid; + } catch (NameNotFoundException e) { + } + return -1; + } } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java index cacbf969e562..bb7906e7c3ae 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java @@ -29,6 +29,10 @@ import androidx.annotation.Nullable; import androidx.core.app.AppComponentFactory; import com.android.systemui.dagger.ContextComponentHelper; +import com.android.systemui.dagger.SysUIComponent; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import javax.inject.Inject; @@ -61,7 +65,7 @@ public class SystemUIAppComponentFactory extends AppComponentFactory { ((ContextInitializer) app).setContextAvailableCallback( context -> { SystemUIFactory.createFromConfig(context); - SystemUIFactory.getInstance().getRootComponent().inject( + SystemUIFactory.getInstance().getSysUIComponent().inject( SystemUIAppComponentFactory.this); } ); @@ -81,8 +85,17 @@ public class SystemUIAppComponentFactory extends AppComponentFactory { ((ContextInitializer) contentProvider).setContextAvailableCallback( context -> { SystemUIFactory.createFromConfig(context); - SystemUIFactory.getInstance().getRootComponent().inject( - contentProvider); + SysUIComponent rootComponent = + SystemUIFactory.getInstance().getSysUIComponent(); + try { + Method injectMethod = rootComponent.getClass() + .getMethod("inject", contentProvider.getClass()); + injectMethod.invoke(rootComponent, contentProvider); + } catch (NoSuchMethodException + | IllegalAccessException + | InvocationTargetException e) { + // no-op + } } ); } @@ -98,7 +111,7 @@ public class SystemUIAppComponentFactory extends AppComponentFactory { if (mComponentHelper == null) { // This shouldn't happen, but is seen on occasion. // Bug filed against framework to take a look: http://b/141008541 - SystemUIFactory.getInstance().getRootComponent().inject( + SystemUIFactory.getInstance().getSysUIComponent().inject( SystemUIAppComponentFactory.this); } Activity activity = mComponentHelper.resolveActivity(className); @@ -116,7 +129,7 @@ public class SystemUIAppComponentFactory extends AppComponentFactory { if (mComponentHelper == null) { // This shouldn't happen, but does when a device is freshly formatted. // Bug filed against framework to take a look: http://b/141008541 - SystemUIFactory.getInstance().getRootComponent().inject( + SystemUIFactory.getInstance().getSysUIComponent().inject( SystemUIAppComponentFactory.this); } Service service = mComponentHelper.resolveService(className); @@ -134,7 +147,7 @@ public class SystemUIAppComponentFactory extends AppComponentFactory { if (mComponentHelper == null) { // This shouldn't happen, but does when a device is freshly formatted. // Bug filed against framework to take a look: http://b/141008541 - SystemUIFactory.getInstance().getRootComponent().inject( + SystemUIFactory.getInstance().getSysUIComponent().inject( SystemUIAppComponentFactory.this); } BroadcastReceiver receiver = mComponentHelper.resolveBroadcastReceiver(className); diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index c84701c9512e..7dcec3d75367 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -32,7 +32,8 @@ import android.util.Log; import android.util.TimingsTraceLog; import com.android.systemui.dagger.ContextComponentHelper; -import com.android.systemui.dagger.SystemUIRootComponent; +import com.android.systemui.dagger.GlobalRootComponent; +import com.android.systemui.dagger.SysUIComponent; import com.android.systemui.dump.DumpManager; import com.android.systemui.util.NotificationChannels; @@ -57,7 +58,8 @@ public class SystemUIApplication extends Application implements private SystemUI[] mServices; private boolean mServicesStarted; private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback; - private SystemUIRootComponent mRootComponent; + private GlobalRootComponent mRootComponent; + private SysUIComponent mSysUIComponent; public SystemUIApplication() { super(); @@ -75,8 +77,9 @@ public class SystemUIApplication extends Application implements log.traceBegin("DependencyInjection"); mContextAvailableCallback.onContextAvailable(this); mRootComponent = SystemUIFactory.getInstance().getRootComponent(); - mComponentHelper = mRootComponent.getContextComponentHelper(); - mBootCompleteCache = mRootComponent.provideBootCacheImpl(); + mSysUIComponent = SystemUIFactory.getInstance().getSysUIComponent(); + mComponentHelper = mSysUIComponent.getContextComponentHelper(); + mBootCompleteCache = mSysUIComponent.provideBootCacheImpl(); log.traceEnd(); // Set the application theme that is inherited by all services. Note that setting the @@ -172,7 +175,7 @@ public class SystemUIApplication extends Application implements } } - final DumpManager dumpManager = mRootComponent.createDumpManager(); + final DumpManager dumpManager = mSysUIComponent.createDumpManager(); Log.v(TAG, "Starting SystemUI services for user " + Process.myUserHandle().getIdentifier() + "."); @@ -215,7 +218,7 @@ public class SystemUIApplication extends Application implements dumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]); } - mRootComponent.getInitController().executePostInitTasks(); + mSysUIComponent.getInitController().executePostInitTasks(); log.traceEnd(); mServicesStarted = true; @@ -224,7 +227,7 @@ public class SystemUIApplication extends Application implements @Override public void onConfigurationChanged(Configuration newConfig) { if (mServicesStarted) { - mRootComponent.getConfigurationController().onConfigurationChanged(newConfig); + mSysUIComponent.getConfigurationController().onConfigurationChanged(newConfig); int len = mServices.length; for (int i = 0; i < len; i++) { if (mServices[i] != null) { diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 1a15c0aae8c1..f5c364947a2f 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -27,21 +27,15 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; -import com.android.systemui.bubbles.BubbleController; -import com.android.systemui.dagger.DaggerSystemUIRootComponent; -import com.android.systemui.dagger.SystemUIRootComponent; +import com.android.systemui.dagger.DaggerGlobalRootComponent; +import com.android.systemui.dagger.GlobalRootComponent; +import com.android.systemui.dagger.SysUIComponent; +import com.android.systemui.dagger.WMComponent; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider; -import com.android.systemui.statusbar.NotificationListener; -import com.android.systemui.statusbar.NotificationMediaManager; -import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NotificationIconAreaController; -import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.KeyguardStateController; import java.util.concurrent.Executor; @@ -53,7 +47,9 @@ public class SystemUIFactory { private static final String TAG = "SystemUIFactory"; static SystemUIFactory mFactory; - private SystemUIRootComponent mRootComponent; + private GlobalRootComponent mRootComponent; + private WMComponent mWMComponent; + private SysUIComponent mSysUIComponent; public static <T extends SystemUIFactory> T getInstance() { return (T) mFactory; @@ -88,24 +84,31 @@ public class SystemUIFactory { public SystemUIFactory() {} private void init(Context context) { - mRootComponent = buildSystemUIRootComponent(context); + mRootComponent = buildGlobalRootComponent(context); + mWMComponent = mRootComponent.getWMComponentBuilder().build(); + // TODO: use WMComponent to pass APIs into the SysUIComponent. + mSysUIComponent = mRootComponent.getSysUIComponent().build(); // Every other part of our codebase currently relies on Dependency, so we // really need to ensure the Dependency gets initialized early on. - Dependency dependency = mRootComponent.createDependency(); + Dependency dependency = mSysUIComponent.createDependency(); dependency.start(); } - protected SystemUIRootComponent buildSystemUIRootComponent(Context context) { - return DaggerSystemUIRootComponent.builder() + protected GlobalRootComponent buildGlobalRootComponent(Context context) { + return DaggerGlobalRootComponent.builder() .context(context) .build(); } - public SystemUIRootComponent getRootComponent() { + public GlobalRootComponent getRootComponent() { return mRootComponent; } + public SysUIComponent getSysUIComponent() { + return mSysUIComponent; + } + /** Returns the list of system UI components that should be started. */ public String[] getSystemUIServiceComponents(Resources resources) { return resources.getStringArray(R.array.config_systemUIServiceComponents); @@ -139,17 +142,4 @@ public class SystemUIFactory { Dependency.get(KeyguardUpdateMonitor.class), bypassController, new Handler(Looper.getMainLooper())); } - - public NotificationIconAreaController createNotificationIconAreaController(Context context, - StatusBar statusBar, - NotificationWakeUpCoordinator wakeUpCoordinator, - KeyguardBypassController keyguardBypassController, - StatusBarStateController statusBarStateController) { - return new NotificationIconAreaController(context, statusBar, statusBarStateController, - wakeUpCoordinator, keyguardBypassController, - Dependency.get(NotificationMediaManager.class), - Dependency.get(NotificationListener.class), - Dependency.get(DozeParameters.class), - Dependency.get(BubbleController.class)); - } } diff --git a/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java b/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java index d5a46de980f9..e26dc7f0545e 100644 --- a/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java +++ b/packages/SystemUI/src/com/android/systemui/UiOffloadThread.java @@ -16,18 +16,19 @@ package com.android.systemui; +import com.android.systemui.dagger.SysUISingleton; + import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import javax.inject.Inject; -import javax.inject.Singleton; /** * Thread that offloads work from the UI thread but that is still perceptible to the user, so the * priority is the same as the main thread. */ -@Singleton +@SysUISingleton public class UiOffloadThread { private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor(); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java index fe07ab67c707..123d678fb185 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java @@ -22,8 +22,7 @@ import android.hardware.display.DisplayManager; import android.view.Display; import com.android.internal.annotations.VisibleForTesting; - -import javax.inject.Singleton; +import com.android.systemui.dagger.SysUISingleton; /** * A class to control {@link MagnificationModeSwitch}. It should show the button UI with following @@ -33,7 +32,7 @@ import javax.inject.Singleton; * <li> The magnification scale is changed by a user.</li> * <ol> */ -@Singleton +@SysUISingleton public class ModeSwitchesController { private final DisplayIdIndexSupplier<MagnificationModeSwitch> mSwitchSupplier; diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java index 0135e4cddc56..e49a5be58cfc 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java @@ -51,18 +51,18 @@ import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActiv import com.android.internal.util.ScreenshotHelper; import com.android.systemui.Dependency; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.recents.Recents; import com.android.systemui.statusbar.phone.StatusBar; import java.util.Locale; import javax.inject.Inject; -import javax.inject.Singleton; /** * Class to register system actions with accessibility framework. */ -@Singleton +@SysUISingleton public class SystemActions extends SystemUI { private static final String TAG = "SystemActions"; @@ -130,8 +130,6 @@ public class SystemActions extends SystemUI { private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; - private Recents mRecents; - private StatusBar mStatusBar; private SystemActionsBroadcastReceiver mReceiver; private Locale mLocale; private AccessibilityManager mA11yManager; @@ -139,8 +137,6 @@ public class SystemActions extends SystemUI { @Inject public SystemActions(Context context) { super(context); - mRecents = Dependency.get(Recents.class); - mStatusBar = Dependency.get(StatusBar.class); mReceiver = new SystemActionsBroadcastReceiver(); mLocale = mContext.getResources().getConfiguration().getLocales().get(0); mA11yManager = (AccessibilityManager) mContext.getSystemService( @@ -317,15 +313,15 @@ public class SystemActions extends SystemUI { } private void handleRecents() { - mRecents.toggleRecentApps(); + Dependency.get(Recents.class).toggleRecentApps(); } private void handleNotifications() { - mStatusBar.animateExpandNotificationsPanel(); + Dependency.get(StatusBar.class).animateExpandNotificationsPanel(); } private void handleQuickSettings() { - mStatusBar.animateExpandSettingsPanel(null); + Dependency.get(StatusBar.class).animateExpandSettingsPanel(null); } private void handlePowerDialog() { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java index d5f74a86fd94..f601c52ba98e 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java @@ -33,11 +33,11 @@ import android.view.accessibility.IWindowMagnificationConnectionCallback; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.CommandQueue; import javax.inject.Inject; -import javax.inject.Singleton; /** * Class to handle the interaction with @@ -45,7 +45,7 @@ import javax.inject.Singleton; * {@link AccessibilityManager#setWindowMagnificationConnection(IWindowMagnificationConnection)} * when {@code IStatusBar#requestWindowMagnificationConnection(boolean)} is called. */ -@Singleton +@SysUISingleton public class WindowMagnification extends SystemUI implements WindowMagnifierCallback, CommandQueue.Callbacks { private static final String TAG = "WindowMagnification"; diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java index 7e5b42653210..93a8df41c673 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java @@ -25,7 +25,9 @@ public class AppOpItem { private int mUid; private String mPackageName; private long mTimeStarted; - private String mState; + private StringBuilder mState; + // This is only used for items with mCode == AppOpsManager.OP_RECORD_AUDIO + private boolean mSilenced; public AppOpItem(int code, int uid, String packageName, long timeStarted) { this.mCode = code; @@ -36,9 +38,8 @@ public class AppOpItem { .append("AppOpItem(") .append("Op code=").append(code).append(", ") .append("UID=").append(uid).append(", ") - .append("Package name=").append(packageName) - .append(")") - .toString(); + .append("Package name=").append(packageName).append(", ") + .append("Paused="); } public int getCode() { @@ -57,8 +58,16 @@ public class AppOpItem { return mTimeStarted; } + public void setSilenced(boolean silenced) { + mSilenced = silenced; + } + + public boolean isSilenced() { + return mSilenced; + } + @Override public String toString() { - return mState; + return mState.append(mSilenced).append(")").toString(); } } diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 6512624f5064..eeb3b28357d7 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -19,6 +19,8 @@ package com.android.systemui.appops; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; +import android.media.AudioManager; +import android.media.AudioRecordingConfiguration; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; @@ -31,6 +33,7 @@ import androidx.annotation.WorkerThread; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dump.DumpManager; import com.android.systemui.util.Assert; @@ -42,7 +45,6 @@ import java.util.List; import java.util.Set; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controller to keep track of applications that have requested access to given App Ops @@ -50,7 +52,7 @@ import javax.inject.Singleton; * It can be subscribed to with callbacks. Additionally, it passes on the information to * NotificationPresenter to be displayed to the user. */ -@Singleton +@SysUISingleton public class AppOpsControllerImpl implements AppOpsController, AppOpsManager.OnOpActiveChangedInternalListener, AppOpsManager.OnOpNotedListener, Dumpable { @@ -63,6 +65,7 @@ public class AppOpsControllerImpl implements AppOpsController, private static final boolean DEBUG = false; private final AppOpsManager mAppOps; + private final AudioManager mAudioManager; private H mBGHandler; private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>(); private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>(); @@ -73,6 +76,9 @@ public class AppOpsControllerImpl implements AppOpsController, private final List<AppOpItem> mActiveItems = new ArrayList<>(); @GuardedBy("mNotedItems") private final List<AppOpItem> mNotedItems = new ArrayList<>(); + @GuardedBy("mActiveItems") + private final SparseArray<ArrayList<AudioRecordingConfiguration>> mRecordingsByUid = + new SparseArray<>(); protected static final int[] OPS = new int[] { AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, @@ -88,7 +94,8 @@ public class AppOpsControllerImpl implements AppOpsController, Context context, @Background Looper bgLooper, DumpManager dumpManager, - PermissionFlagsCache cache + PermissionFlagsCache cache, + AudioManager audioManager ) { mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mFlagsCache = cache; @@ -97,6 +104,7 @@ public class AppOpsControllerImpl implements AppOpsController, for (int i = 0; i < numOps; i++) { mCallbacksByCode.put(OPS[i], new ArraySet<>()); } + mAudioManager = audioManager; dumpManager.registerDumpable(TAG, this); } @@ -111,12 +119,19 @@ public class AppOpsControllerImpl implements AppOpsController, if (listening) { mAppOps.startWatchingActive(OPS, this); mAppOps.startWatchingNoted(OPS, this); + mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler); + mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged( + mAudioManager.getActiveRecordingConfigurations())); + } else { mAppOps.stopWatchingActive(this); mAppOps.stopWatchingNoted(this); + mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback); + mBGHandler.removeCallbacksAndMessages(null); // null removes all synchronized (mActiveItems) { mActiveItems.clear(); + mRecordingsByUid.clear(); } synchronized (mNotedItems) { mNotedItems.clear(); @@ -189,9 +204,12 @@ public class AppOpsControllerImpl implements AppOpsController, AppOpItem item = getAppOpItemLocked(mActiveItems, code, uid, packageName); if (item == null && active) { item = new AppOpItem(code, uid, packageName, System.currentTimeMillis()); + if (code == AppOpsManager.OP_RECORD_AUDIO) { + item.setSilenced(isAnyRecordingPausedLocked(uid)); + } mActiveItems.add(item); if (DEBUG) Log.w(TAG, "Added item: " + item.toString()); - return true; + return !item.isSilenced(); } else if (item != null && !active) { mActiveItems.remove(item); if (DEBUG) Log.w(TAG, "Removed item: " + item.toString()); @@ -215,7 +233,7 @@ public class AppOpsControllerImpl implements AppOpsController, active = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null; } if (!active) { - notifySuscribers(code, uid, packageName, false); + notifySuscribersWorker(code, uid, packageName, false); } } @@ -324,7 +342,7 @@ public class AppOpsControllerImpl implements AppOpsController, AppOpItem item = mActiveItems.get(i); if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId) - && isUserVisible(item)) { + && isUserVisible(item) && !item.isSilenced()) { list.add(item); } } @@ -343,6 +361,10 @@ public class AppOpsControllerImpl implements AppOpsController, return list; } + private void notifySuscribers(int code, int uid, String packageName, boolean active) { + mBGHandler.post(() -> notifySuscribersWorker(code, uid, packageName, active)); + } + @Override public void onOpActiveChanged(int code, int uid, String packageName, boolean active) { if (DEBUG) { @@ -360,7 +382,7 @@ public class AppOpsControllerImpl implements AppOpsController, // If active is false, we only send the update if the op is not actively noted (prevent // early removal) if (!alsoNoted) { - mBGHandler.post(() -> notifySuscribers(code, uid, packageName, active)); + notifySuscribers(code, uid, packageName, active); } } @@ -378,11 +400,11 @@ public class AppOpsControllerImpl implements AppOpsController, alsoActive = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null; } if (!alsoActive) { - mBGHandler.post(() -> notifySuscribers(code, uid, packageName, true)); + notifySuscribers(code, uid, packageName, true); } } - private void notifySuscribers(int code, int uid, String packageName, boolean active) { + private void notifySuscribersWorker(int code, int uid, String packageName, boolean active) { if (mCallbacksByCode.contains(code) && isUserVisible(code, uid, packageName)) { if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName); for (Callback cb: mCallbacksByCode.get(code)) { @@ -408,6 +430,61 @@ public class AppOpsControllerImpl implements AppOpsController, } + private boolean isAnyRecordingPausedLocked(int uid) { + List<AudioRecordingConfiguration> configs = mRecordingsByUid.get(uid); + if (configs == null) return false; + int configsNum = configs.size(); + for (int i = 0; i < configsNum; i++) { + AudioRecordingConfiguration config = configs.get(i); + if (config.isClientSilenced()) return true; + } + return false; + } + + private void updateRecordingPausedStatus() { + synchronized (mActiveItems) { + int size = mActiveItems.size(); + for (int i = 0; i < size; i++) { + AppOpItem item = mActiveItems.get(i); + if (item.getCode() == AppOpsManager.OP_RECORD_AUDIO) { + boolean paused = isAnyRecordingPausedLocked(item.getUid()); + if (item.isSilenced() != paused) { + item.setSilenced(paused); + notifySuscribers( + item.getCode(), + item.getUid(), + item.getPackageName(), + !item.isSilenced() + ); + } + } + } + } + } + + private AudioManager.AudioRecordingCallback mAudioRecordingCallback = + new AudioManager.AudioRecordingCallback() { + @Override + public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) { + synchronized (mActiveItems) { + mRecordingsByUid.clear(); + final int recordingsCount = configs.size(); + for (int i = 0; i < recordingsCount; i++) { + AudioRecordingConfiguration recording = configs.get(i); + + ArrayList<AudioRecordingConfiguration> recordings = mRecordingsByUid.get( + recording.getClientUid()); + if (recordings == null) { + recordings = new ArrayList<>(); + mRecordingsByUid.put(recording.getClientUid(), recordings); + } + recordings.add(recording); + } + } + updateRecordingPausedStatus(); + } + }; + protected class H extends Handler { H(Looper looper) { super(looper); diff --git a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt index 45ed78f750be..3fd838be781d 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt +++ b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt @@ -19,11 +19,11 @@ package com.android.systemui.appops import android.content.pm.PackageManager import android.os.UserHandle import androidx.annotation.WorkerThread +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.util.Assert import java.util.concurrent.Executor import javax.inject.Inject -import javax.inject.Singleton private data class PermissionFlagKey( val permission: String, @@ -37,7 +37,7 @@ private data class PermissionFlagKey( * After a specific `{permission, package, uid}` has been requested, updates to it will be tracked, * and changes to the uid will trigger new requests (in the background). */ -@Singleton +@SysUISingleton class PermissionFlagsCache @Inject constructor( private val packageManager: PackageManager, @Background private val executor: Executor diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java index 7ae3e73caa23..4390d513a9fb 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java @@ -33,9 +33,10 @@ import com.android.internal.app.AssistUtils; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.statusbar.phone.NavigationModeController; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -45,7 +46,6 @@ import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; -import javax.inject.Singleton; import dagger.Lazy; @@ -55,7 +55,7 @@ import dagger.Lazy; * Controls when visual handles for Assistant gesture affordance should be shown or hidden using an * {@link AssistHandleBehavior}. */ -@Singleton +@SysUISingleton public final class AssistHandleBehaviorController implements AssistHandleCallbacks, Dumpable { private static final String TAG = "AssistHandleBehavior"; diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java index ccca447ad842..5d8ec4bb330b 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java @@ -21,6 +21,7 @@ import android.content.Context; import androidx.annotation.Nullable; import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -29,7 +30,6 @@ import com.android.systemui.shared.system.QuickStepContract; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; @@ -37,7 +37,7 @@ import dagger.Lazy; * Assistant Handle behavior that makes Assistant handles show/hide when the home handle is * shown/hidden, respectively. */ -@Singleton +@SysUISingleton final class AssistHandleLikeHomeBehavior implements BehaviorController { private final StatusBarStateController.StateListener mStatusBarStateListener = diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java index df913f9ce9cd..86d3254dc7f9 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java @@ -19,12 +19,12 @@ package com.android.systemui.assist; import android.content.Context; import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController; +import com.android.systemui.dagger.SysUISingleton; import javax.inject.Inject; -import javax.inject.Singleton; /** Assistant handle behavior that hides the Assistant handles. */ -@Singleton +@SysUISingleton final class AssistHandleOffBehavior implements BehaviorController { @Inject diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java index 8e49d584788a..1b2e4c6a595e 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java @@ -36,6 +36,7 @@ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.systemui.BootCompleteCache; import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -54,7 +55,6 @@ import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.inject.Named; -import javax.inject.Singleton; import dagger.Lazy; @@ -63,7 +63,7 @@ import dagger.Lazy; * shows the handles when on lockscreen, and shows the handles temporarily when changing tasks or * entering overview. */ -@Singleton +@SysUISingleton final class AssistHandleReminderExpBehavior implements BehaviorController { private static final String LEARNING_TIME_ELAPSED_KEY = "reminder_exp_learning_time_elapsed"; diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java index 5010f319f8b4..f19e53f03849 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java @@ -30,7 +30,7 @@ import android.view.animation.PathInterpolator; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.CornerHandleView; import com.android.systemui.R; -import com.android.systemui.statusbar.phone.NavigationBarTransitions; +import com.android.systemui.navigationbar.NavigationBarTransitions; /** * A class for managing Assistant handle show, hide and animation. diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistLogger.kt b/packages/SystemUI/src/com/android/systemui/assist/AssistLogger.kt index 08edad3a7f7f..4d0fd4313209 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistLogger.kt @@ -28,11 +28,11 @@ import com.android.internal.util.FrameworkStatsLog import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.assist.AssistantInvocationEvent.Companion.deviceStateFromLegacyDeviceState import com.android.systemui.assist.AssistantInvocationEvent.Companion.eventFromLegacyInvocationType +import com.android.systemui.dagger.SysUISingleton import javax.inject.Inject -import javax.inject.Singleton /** Class for reporting events related to Assistant sessions. */ -@Singleton +@SysUISingleton open class AssistLogger @Inject constructor( protected val context: Context, protected val uiEventLogger: UiEventLogger, diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 6d179f27f4fb..e85cafaf47ac 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -46,6 +46,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.settingslib.applications.InterestingConfigChanges; import com.android.systemui.R; import com.android.systemui.assist.ui.DefaultUiController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.model.SysUiState; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.statusbar.CommandQueue; @@ -53,14 +54,13 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; /** * Class to manage everything related to assist in SystemUI. */ -@Singleton +@SysUISingleton public class AssistManager { /** diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java index 6f5a17dca432..ef43f87d20e8 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java @@ -25,13 +25,13 @@ import androidx.annotation.Nullable; import androidx.slice.Clock; import com.android.internal.app.AssistUtils; -import com.android.systemui.statusbar.NavigationBarController; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.navigationbar.NavigationBarController; import java.util.EnumMap; import java.util.Map; import javax.inject.Named; -import javax.inject.Singleton; import dagger.Module; import dagger.Provides; @@ -44,7 +44,7 @@ public abstract class AssistModule { static final String UPTIME_NAME = "uptime"; @Provides - @Singleton + @SysUISingleton @Named(ASSIST_HANDLE_THREAD_NAME) static Handler provideBackgroundHandler() { final HandlerThread backgroundHandlerThread = @@ -54,7 +54,7 @@ public abstract class AssistModule { } @Provides - @Singleton + @SysUISingleton static Map<AssistHandleBehavior, AssistHandleBehaviorController.BehaviorController> provideAssistHandleBehaviorControllerMap( AssistHandleOffBehavior offBehavior, @@ -76,13 +76,13 @@ public abstract class AssistModule { } @Provides - @Singleton + @SysUISingleton static AssistUtils provideAssistUtils(Context context) { return new AssistUtils(context); } @Provides - @Singleton + @SysUISingleton @Named(UPTIME_NAME) static Clock provideSystemClock() { return SystemClock::uptimeMillis; diff --git a/packages/SystemUI/src/com/android/systemui/assist/DeviceConfigHelper.java b/packages/SystemUI/src/com/android/systemui/assist/DeviceConfigHelper.java index 86b7c748ea77..4d5c44c9e88d 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/DeviceConfigHelper.java +++ b/packages/SystemUI/src/com/android/systemui/assist/DeviceConfigHelper.java @@ -22,17 +22,18 @@ import android.provider.DeviceConfig; import androidx.annotation.Nullable; +import com.android.systemui.dagger.SysUISingleton; + import java.util.concurrent.Executor; import javax.inject.Inject; -import javax.inject.Singleton; /** * Wrapper class for retrieving System UI device configuration values. * * Can be mocked in tests for ease of testing the effects of particular values. */ -@Singleton +@SysUISingleton public class DeviceConfigHelper { @Inject diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java index 257ad50eff61..50d559b7aeab 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java @@ -30,6 +30,7 @@ import androidx.annotation.Nullable; import com.android.systemui.BootCompleteCache; import com.android.systemui.Dependency; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.PackageManagerWrapper; @@ -42,12 +43,11 @@ import java.util.List; import java.util.Optional; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; /** Class to monitor and report the state of the phone. */ -@Singleton +@SysUISingleton public final class PhoneStateMonitor { public static final int PHONE_STATE_AOD1 = 1; diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java index 05f3617dd1d6..1d9009668b47 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java +++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java @@ -41,18 +41,18 @@ import com.android.systemui.assist.AssistHandleViewController; import com.android.systemui.assist.AssistLogger; import com.android.systemui.assist.AssistManager; import com.android.systemui.assist.AssistantSessionEvent; -import com.android.systemui.statusbar.NavigationBarController; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.navigationbar.NavigationBarController; import java.util.Locale; import javax.inject.Inject; -import javax.inject.Singleton; /** * Default UiController implementation. Shows white edge lights along the bottom of the phone, * expanding from the corners to meet in the center. */ -@Singleton +@SysUISingleton public class DefaultUiController implements AssistManager.UiController { private static final String TAG = "DefaultUiController"; diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java b/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java index e5121a8ea35c..ac39ed501811 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java +++ b/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java @@ -33,9 +33,9 @@ import android.view.View; import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.statusbar.NavigationBarController; -import com.android.systemui.statusbar.phone.NavigationBarFragment; -import com.android.systemui.statusbar.phone.NavigationBarTransitions; +import com.android.systemui.navigationbar.NavigationBarController; +import com.android.systemui.navigationbar.NavigationBar; +import com.android.systemui.navigationbar.NavigationBarTransitions; import java.util.ArrayList; @@ -284,7 +284,7 @@ public class InvocationLightsView extends View return; } - NavigationBarFragment navBar = controller.getDefaultNavigationBarFragment(); + NavigationBar navBar = controller.getDefaultNavigationBar(); if (navBar == null) { return; } @@ -301,7 +301,7 @@ public class InvocationLightsView extends View return; } - NavigationBarFragment navBar = controller.getDefaultNavigationBarFragment(); + NavigationBar navBar = controller.getDefaultNavigationBar(); if (navBar == null) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 361ea674cead..ea18b11413ef 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -49,25 +49,28 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.doze.DozeReceiver; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the * appropriate biometric UI (e.g. BiometricDialogView). */ -@Singleton +@SysUISingleton public class AuthController extends SystemUI implements CommandQueue.Callbacks, - AuthDialogCallback { + AuthDialogCallback, DozeReceiver { - private static final String TAG = "BiometricPrompt/AuthController"; + private static final String TAG = "AuthController"; private static final boolean DEBUG = true; private final CommandQueue mCommandQueue; + private final StatusBarStateController mStatusBarStateController; private final Injector mInjector; // TODO: These should just be saved from onSaveState @@ -77,6 +80,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, private Handler mHandler = new Handler(Looper.getMainLooper()); private WindowManager mWindowManager; + @Nullable private UdfpsController mUdfpsController; @VisibleForTesting IActivityTaskManager mActivityTaskManager; @@ -143,6 +147,13 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, }; @Override + public void dozeTimeTick() { + if (mUdfpsController != null) { + mUdfpsController.dozeTimeTick(); + } + } + + @Override public void onTryAgainPressed() { if (mReceiver == null) { Log.e(TAG, "onTryAgainPressed: Receiver is null"); @@ -251,14 +262,17 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } @Inject - public AuthController(Context context, CommandQueue commandQueue) { - this(context, commandQueue, new Injector()); + public AuthController(Context context, CommandQueue commandQueue, + StatusBarStateController statusBarStateController) { + this(context, commandQueue, statusBarStateController, new Injector()); } @VisibleForTesting - AuthController(Context context, CommandQueue commandQueue, Injector injector) { + AuthController(Context context, CommandQueue commandQueue, + StatusBarStateController statusBarStateController, Injector injector) { super(context); mCommandQueue = commandQueue; + mStatusBarStateController = statusBarStateController; mInjector = injector; IntentFilter filter = new IntentFilter(); @@ -280,7 +294,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, fpm.getSensorProperties(); for (FingerprintSensorProperties props : fingerprintSensorProperties) { if (props.sensorType == FingerprintSensorProperties.TYPE_UDFPS) { - mUdfpsController = new UdfpsController(mContext, mWindowManager); + mUdfpsController = new UdfpsController(mContext, mStatusBarStateController); break; } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 739c2b155444..82fb80892ab1 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -16,23 +16,32 @@ package com.android.systemui.biometrics; +import android.annotation.NonNull; import android.annotation.SuppressLint; +import android.content.ContentResolver; import android.content.Context; +import android.content.res.TypedArray; import android.graphics.PixelFormat; import android.graphics.Point; import android.hardware.fingerprint.FingerprintManager; -import android.hardware.fingerprint.IFingerprintService; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.Handler; import android.os.Looper; -import android.os.RemoteException; +import android.os.PowerManager; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.TextUtils; import android.util.Log; +import android.util.MathUtils; +import android.util.Spline; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.WindowManager; -import android.widget.LinearLayout; +import com.android.internal.BrightnessSynchronizer; import com.android.systemui.R; +import com.android.systemui.doze.DozeReceiver; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import java.io.FileWriter; import java.io.IOException; @@ -41,19 +50,34 @@ import java.io.IOException; * Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events, * and coordinates triggering of the high-brightness mode (HBM). */ -class UdfpsController { +class UdfpsController implements DozeReceiver { private static final String TAG = "UdfpsController"; + // Gamma approximation for the sRGB color space. + private static final float DISPLAY_GAMMA = 2.2f; - private final Context mContext; private final FingerprintManager mFingerprintManager; private final WindowManager mWindowManager; + private final ContentResolver mContentResolver; private final Handler mHandler; - - private UdfpsView mView; - private WindowManager.LayoutParams mLayoutParams; - private String mHbmPath; - private String mHbmEnableCommand; - private String mHbmDisableCommand; + private final WindowManager.LayoutParams mLayoutParams; + private final UdfpsView mView; + // Debugfs path to control the high-brightness mode. + private final String mHbmPath; + private final String mHbmEnableCommand; + private final String mHbmDisableCommand; + private final boolean mHbmSupported; + // Brightness in nits in the high-brightness mode. + private final float mHbmNits; + // A spline mapping from the device's backlight value, normalized to the range [0, 1.0], to a + // brightness in nits. + private final Spline mBacklightToNitsSpline; + // A spline mapping from a value in nits to a backlight value of a hypothetical panel whose + // maximum backlight value corresponds to our panel's high-brightness mode. + // The output is normalized to the range [0, 1.0]. + private Spline mNitsToHbmBacklightSpline; + // Default non-HBM backlight value normalized to the range [0, 1.0]. Used as a fallback when the + // actual brightness value cannot be retrieved. + private final float mDefaultBrightness; private boolean mIsOverlayShowing; public class UdfpsOverlayController extends IUdfpsOverlayController.Stub { @@ -66,26 +90,33 @@ class UdfpsController { public void hideUdfpsOverlay() { UdfpsController.this.hideUdfpsOverlay(); } + + @Override + public void setDebugMessage(String message) { + mView.setDebugMessage(message); + } } @SuppressLint("ClickableViewAccessibility") private final UdfpsView.OnTouchListener mOnTouchListener = (v, event) -> { + UdfpsView view = (UdfpsView) v; + final boolean isFingerDown = view.isScrimShowing(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: - boolean isValidTouch = mView.isValidTouch(event.getX(), event.getY(), + final boolean isValidTouch = view.isValidTouch(event.getX(), event.getY(), event.getPressure()); - if (!mView.isFingerDown() && isValidTouch) { + if (!isFingerDown && isValidTouch) { onFingerDown((int) event.getX(), (int) event.getY(), event.getTouchMinor(), event.getTouchMajor()); - } else if (mView.isFingerDown() && !isValidTouch) { + } else if (isFingerDown && !isValidTouch) { onFingerUp(); } return true; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - if (mView.isFingerDown()) { + if (isFingerDown) { onFingerUp(); } return true; @@ -95,94 +126,200 @@ class UdfpsController { } }; - UdfpsController(Context context, WindowManager windowManager) { - mContext = context; + UdfpsController(@NonNull Context context, + @NonNull StatusBarStateController statusBarStateController) { mFingerprintManager = context.getSystemService(FingerprintManager.class); - mWindowManager = windowManager; + mWindowManager = context.getSystemService(WindowManager.class); + mContentResolver = context.getContentResolver(); mHandler = new Handler(Looper.getMainLooper()); - start(); - } + mLayoutParams = createLayoutParams(context); - private void start() { - Log.v(TAG, "start"); + mView = (UdfpsView) LayoutInflater.from(context).inflate(R.layout.udfps_view, null, false); - Point displaySize = new Point(); - mWindowManager.getDefaultDisplay().getRealSize(displaySize); - // TODO(b/160025856): move to the "dump" method. - Log.v(TAG, "UdfpsController | display size: " + displaySize.x + "x" - + displaySize.y); + mHbmPath = context.getResources().getString(R.string.udfps_hbm_sysfs_path); + mHbmEnableCommand = context.getResources().getString(R.string.udfps_hbm_enable_command); + mHbmDisableCommand = context.getResources().getString(R.string.udfps_hbm_disable_command); - mLayoutParams = new WindowManager.LayoutParams( - displaySize.x, - displaySize.y, - // TODO(b/152419866): Use the UDFPS window type when it becomes available. - WindowManager.LayoutParams.TYPE_BOOT_PROGRESS, - WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, - PixelFormat.TRANSLUCENT); - mLayoutParams.setTitle(TAG); - mLayoutParams.windowAnimations = 0; + mHbmSupported = !TextUtils.isEmpty(mHbmPath); + mView.setHbmSupported(mHbmSupported); + statusBarStateController.addCallback(mView); + + // This range only consists of the minimum and maximum values, which only cover + // non-high-brightness mode. + float[] nitsRange = toFloatArray(context.getResources().obtainTypedArray( + com.android.internal.R.array.config_screenBrightnessNits)); + + // The last value of this range corresponds to the high-brightness mode. + float[] nitsAutoBrightnessValues = toFloatArray(context.getResources().obtainTypedArray( + com.android.internal.R.array.config_autoBrightnessDisplayValuesNits)); + + mHbmNits = nitsAutoBrightnessValues[nitsAutoBrightnessValues.length - 1]; + float[] hbmNitsRange = {nitsRange[0], mHbmNits}; - LinearLayout layout = new LinearLayout(mContext); - layout.setLayoutParams(mLayoutParams); - mView = (UdfpsView) LayoutInflater.from(mContext).inflate(R.layout.udfps_view, layout, - false); - mView.setOnTouchListener(mOnTouchListener); + // This range only consists of the minimum and maximum backlight values, which only apply + // in non-high-brightness mode. + float[] normalizedBacklightRange = normalizeBacklightRange( + context.getResources().getIntArray( + com.android.internal.R.array.config_screenBrightnessBacklight)); - mHbmPath = mContext.getResources().getString(R.string.udfps_hbm_sysfs_path); - mHbmEnableCommand = mContext.getResources().getString(R.string.udfps_hbm_enable_command); - mHbmDisableCommand = mContext.getResources().getString(R.string.udfps_hbm_disable_command); + mBacklightToNitsSpline = Spline.createSpline(normalizedBacklightRange, nitsRange); + mNitsToHbmBacklightSpline = Spline.createSpline(hbmNitsRange, normalizedBacklightRange); + mDefaultBrightness = obtainDefaultBrightness(context); + + // TODO(b/160025856): move to the "dump" method. + Log.v(TAG, String.format("ctor | mNitsRange: [%f, %f]", nitsRange[0], nitsRange[1])); + Log.v(TAG, String.format("ctor | mHbmNitsRange: [%f, %f]", hbmNitsRange[0], + hbmNitsRange[1])); + Log.v(TAG, String.format("ctor | mNormalizedBacklightRange: [%f, %f]", + normalizedBacklightRange[0], normalizedBacklightRange[1])); mFingerprintManager.setUdfpsOverlayController(new UdfpsOverlayController()); mIsOverlayShowing = false; } + @Override + public void dozeTimeTick() { + mView.dozeTimeTick(); + } + private void showUdfpsOverlay() { mHandler.post(() -> { - Log.v(TAG, "showUdfpsOverlay | adding window"); if (!mIsOverlayShowing) { try { + Log.v(TAG, "showUdfpsOverlay | adding window"); mWindowManager.addView(mView, mLayoutParams); mIsOverlayShowing = true; + mView.setOnTouchListener(mOnTouchListener); } catch (RuntimeException e) { Log.e(TAG, "showUdfpsOverlay | failed to add window", e); } + } else { + Log.v(TAG, "showUdfpsOverlay | the overlay is already showing"); } }); } private void hideUdfpsOverlay() { - onFingerUp(); mHandler.post(() -> { - Log.v(TAG, "hideUdfpsOverlay | removing window"); if (mIsOverlayShowing) { + Log.v(TAG, "hideUdfpsOverlay | removing window"); + mView.setOnTouchListener(null); + // Reset the controller back to its starting state. + onFingerUp(); mWindowManager.removeView(mView); mIsOverlayShowing = false; + } else { + Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden"); } }); } + // Returns a value in the range of [0, 255]. + private int computeScrimOpacity() { + // Backlight setting can be NaN, -1.0f, and [0.0f, 1.0f]. + float backlightSetting = Settings.System.getFloatForUser(mContentResolver, + Settings.System.SCREEN_BRIGHTNESS_FLOAT, mDefaultBrightness, + UserHandle.USER_CURRENT); + + // Constrain the backlight setting to [0.0f, 1.0f]. + float backlightValue = MathUtils.constrain(backlightSetting, + PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX); + + // Interpolate the backlight value to nits. + float nits = mBacklightToNitsSpline.interpolate(backlightValue); + + // Interpolate nits to a backlight value for a panel with enabled HBM. + float interpolatedHbmBacklightValue = mNitsToHbmBacklightSpline.interpolate(nits); + + float gammaCorrectedHbmBacklightValue = (float) Math.pow(interpolatedHbmBacklightValue, + 1.0f / DISPLAY_GAMMA); + float scrimOpacity = PowerManager.BRIGHTNESS_MAX - gammaCorrectedHbmBacklightValue; + + // Interpolate the opacity value from [0.0f, 1.0f] to [0, 255]. + return BrightnessSynchronizer.brightnessFloatToInt(scrimOpacity); + } + private void onFingerDown(int x, int y, float minor, float major) { + mView.setScrimAlpha(computeScrimOpacity()); + mView.showScrimAndDot(); try { - FileWriter fw = new FileWriter(mHbmPath); - fw.write(mHbmEnableCommand); - fw.close(); + if (mHbmSupported) { + FileWriter fw = new FileWriter(mHbmPath); + fw.write(mHbmEnableCommand); + fw.close(); + } + mFingerprintManager.onFingerDown(x, y, minor, major); } catch (IOException e) { + mView.hideScrimAndDot(); Log.e(TAG, "onFingerDown | failed to enable HBM: " + e.getMessage()); } - mView.onFingerDown(); - mFingerprintManager.onFingerDown(x, y, minor, major); } private void onFingerUp() { mFingerprintManager.onFingerUp(); - mView.onFingerUp(); - try { - FileWriter fw = new FileWriter(mHbmPath); - fw.write(mHbmDisableCommand); - fw.close(); - } catch (IOException e) { - Log.e(TAG, "onFingerUp | failed to disable HBM: " + e.getMessage()); + // Hiding the scrim before disabling HBM results in less noticeable flicker. + mView.hideScrimAndDot(); + if (mHbmSupported) { + try { + FileWriter fw = new FileWriter(mHbmPath); + fw.write(mHbmDisableCommand); + fw.close(); + } catch (IOException e) { + mView.showScrimAndDot(); + Log.e(TAG, "onFingerUp | failed to disable HBM: " + e.getMessage()); + } + } + } + + private static WindowManager.LayoutParams createLayoutParams(Context context) { + Point displaySize = new Point(); + context.getDisplay().getRealSize(displaySize); + // TODO(b/160025856): move to the "dump" method. + Log.v(TAG, "createLayoutParams | display size: " + displaySize.x + "x" + + displaySize.y); + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + displaySize.x, + displaySize.y, + // TODO(b/152419866): Use the UDFPS window type when it becomes available. + WindowManager.LayoutParams.TYPE_BOOT_PROGRESS, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, + PixelFormat.TRANSLUCENT); + lp.setTitle(TAG); + lp.setFitInsetsTypes(0); + return lp; + } + + private static float obtainDefaultBrightness(Context context) { + PowerManager powerManager = context.getSystemService(PowerManager.class); + if (powerManager == null) { + Log.e(TAG, "PowerManager is unavailable. Can't obtain default brightness."); + return 0f; + } + return MathUtils.constrain(powerManager.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT), PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX); + } + + private static float[] toFloatArray(TypedArray array) { + final int n = array.length(); + float[] vals = new float[n]; + for (int i = 0; i < n; i++) { + vals[i] = array.getFloat(i, PowerManager.BRIGHTNESS_OFF_FLOAT); + } + array.recycle(); + return vals; + } + + private static float[] normalizeBacklightRange(int[] backlight) { + final int n = backlight.length; + float[] normalizedBacklight = new float[n]; + for (int i = 0; i < n; i++) { + normalizedBacklight[i] = BrightnessSynchronizer.brightnessIntToFloat(backlight[i]); } + return normalizedBacklight; } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index 8190550a74fc..d7e91384f049 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -16,6 +16,8 @@ package com.android.systemui.biometrics; +import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; + import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; @@ -23,34 +25,57 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; +import android.util.MathUtils; import android.view.View; import android.view.ViewTreeObserver; import com.android.systemui.R; +import com.android.systemui.doze.DozeReceiver; +import com.android.systemui.plugins.statusbar.StatusBarStateController; /** * A full screen view with a configurable illumination dot and scrim. */ -public class UdfpsView extends View { +public class UdfpsView extends View implements DozeReceiver, + StatusBarStateController.StateListener { private static final String TAG = "UdfpsView"; + // Values in pixels. + private static final float SENSOR_SHADOW_RADIUS = 2.0f; + private static final float SENSOR_OUTLINE_WIDTH = 2.0f; + + private static final int DEBUG_TEXT_SIZE_PX = 32; + private final Rect mScrimRect; private final Paint mScrimPaint; + private final Paint mDebugTextPaint; - private float mSensorX; - private float mSensorY; private final RectF mSensorRect; private final Paint mSensorPaint; private final float mSensorRadius; - private final float mSensorMarginBottom; + private final float mSensorCenterY; private final float mSensorTouchAreaCoefficient; + private final int mMaxBurnInOffsetX; + private final int mMaxBurnInOffsetY; private final Rect mTouchableRegion; private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener; - private boolean mIsFingerDown; + // This is calculated from the screen's dimensions at runtime, as opposed to mSensorCenterY, + // which is defined in layout.xml + private float mSensorCenterX; + + // AOD anti-burn-in offsets + private float mInterpolatedDarkAmount; + private float mBurnInOffsetX; + private float mBurnInOffsetY; + + private boolean mIsScrimShowing; + private boolean mHbmSupported; + private String mDebugMessage; public UdfpsView(Context context, AttributeSet attrs) { super(context, attrs); @@ -61,7 +86,7 @@ public class UdfpsView extends View { if (!a.hasValue(R.styleable.UdfpsView_sensorRadius)) { throw new IllegalArgumentException("UdfpsView must contain sensorRadius"); } - if (!a.hasValue(R.styleable.UdfpsView_sensorMarginBottom)) { + if (!a.hasValue(R.styleable.UdfpsView_sensorCenterY)) { throw new IllegalArgumentException("UdfpsView must contain sensorMarginBottom"); } if (!a.hasValue(R.styleable.UdfpsView_sensorTouchAreaCoefficient)) { @@ -69,22 +94,35 @@ public class UdfpsView extends View { "UdfpsView must contain sensorTouchAreaCoefficient"); } mSensorRadius = a.getDimension(R.styleable.UdfpsView_sensorRadius, 0f); - mSensorMarginBottom = a.getDimension(R.styleable.UdfpsView_sensorMarginBottom, 0f); + mSensorCenterY = a.getDimension(R.styleable.UdfpsView_sensorCenterY, 0f); mSensorTouchAreaCoefficient = a.getFloat( R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f); } finally { a.recycle(); } + mMaxBurnInOffsetX = getResources() + .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x); + mMaxBurnInOffsetY = getResources() + .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); mScrimRect = new Rect(); mScrimPaint = new Paint(0 /* flags */); - mScrimPaint.setARGB(110 /* a */, 0 /* r */, 0 /* g */, 0 /* b */); + mScrimPaint.setColor(Color.BLACK); mSensorRect = new RectF(); mSensorPaint = new Paint(0 /* flags */); + mSensorPaint.setAntiAlias(true); mSensorPaint.setColor(Color.WHITE); mSensorPaint.setStyle(Paint.Style.STROKE); + mSensorPaint.setStrokeWidth(SENSOR_OUTLINE_WIDTH); + mSensorPaint.setShadowLayer(SENSOR_SHADOW_RADIUS, 0, 0, Color.BLACK); + mSensorPaint.setAntiAlias(true); + + mDebugTextPaint = new Paint(); + mDebugTextPaint.setAntiAlias(true); + mDebugTextPaint.setColor(Color.BLUE); + mDebugTextPaint.setTextSize(DEBUG_TEXT_SIZE_PX); mTouchableRegion = new Rect(); mInsetsListener = internalInsetsInfo -> { @@ -93,7 +131,30 @@ public class UdfpsView extends View { internalInsetsInfo.touchableRegion.set(mTouchableRegion); }; - mIsFingerDown = false; + mIsScrimShowing = false; + } + + @Override + public void dozeTimeTick() { + updateAodPosition(); + } + + @Override + public void onDozeAmountChanged(float linear, float eased) { + mInterpolatedDarkAmount = eased; + updateAodPosition(); + } + + private void updateAodPosition() { + mBurnInOffsetX = MathUtils.lerp(0f, + getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */) + - mMaxBurnInOffsetX, + mInterpolatedDarkAmount); + mBurnInOffsetY = MathUtils.lerp(0f, + getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */) + - 0.5f * mMaxBurnInOffsetY, + mInterpolatedDarkAmount); + postInvalidate(); } @Override @@ -104,10 +165,11 @@ public class UdfpsView extends View { final int h = getLayoutParams().height; final int w = getLayoutParams().width; mScrimRect.set(0 /* left */, 0 /* top */, w, h); - mSensorX = w / 2f; - mSensorY = h - mSensorMarginBottom - mSensorRadius; - mSensorRect.set(mSensorX - mSensorRadius, mSensorY - mSensorRadius, - mSensorX + mSensorRadius, mSensorY + mSensorRadius); + mSensorCenterX = w / 2f; + mSensorRect.set(mSensorCenterX - mSensorRadius, mSensorCenterY - mSensorRadius, + mSensorCenterX + mSensorRadius, mSensorCenterY + mSensorRadius); + + // Sets mTouchableRegion with rounded up values from mSensorRect. mSensorRect.roundOut(mTouchableRegion); getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener); @@ -123,32 +185,55 @@ public class UdfpsView extends View { @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); - if (mIsFingerDown) { + + if (mIsScrimShowing && mHbmSupported) { + // Only draw the scrim if HBM is supported. canvas.drawRect(mScrimRect, mScrimPaint); } + + // Translation should affect everything but the scrim. + canvas.save(); + canvas.translate(mBurnInOffsetX, mBurnInOffsetY); + if (!TextUtils.isEmpty(mDebugMessage)) { + canvas.drawText(mDebugMessage, 0, 160, mDebugTextPaint); + } canvas.drawOval(mSensorRect, mSensorPaint); + canvas.restore(); + } + + void setHbmSupported(boolean hbmSupported) { + mHbmSupported = hbmSupported; + } + + void setDebugMessage(String message) { + mDebugMessage = message; + postInvalidate(); } boolean isValidTouch(float x, float y, float pressure) { - return x > (mSensorX - mSensorRadius * mSensorTouchAreaCoefficient) - && x < (mSensorX + mSensorRadius * mSensorTouchAreaCoefficient) - && y > (mSensorY - mSensorRadius * mSensorTouchAreaCoefficient) - && y < (mSensorY + mSensorRadius * mSensorTouchAreaCoefficient); + return x > (mSensorCenterX - mSensorRadius * mSensorTouchAreaCoefficient) + && x < (mSensorCenterX + mSensorRadius * mSensorTouchAreaCoefficient) + && y > (mSensorCenterY - mSensorRadius * mSensorTouchAreaCoefficient) + && y < (mSensorCenterY + mSensorRadius * mSensorTouchAreaCoefficient); } - boolean isFingerDown() { - return mIsFingerDown; + void setScrimAlpha(int alpha) { + mScrimPaint.setAlpha(alpha); } - void onFingerDown() { - mIsFingerDown = true; + boolean isScrimShowing() { + return mIsScrimShowing; + } + + void showScrimAndDot() { + mIsScrimShowing = true; mSensorPaint.setStyle(Paint.Style.FILL); - postInvalidate(); + invalidate(); } - void onFingerUp() { - mIsFingerDown = false; + void hideScrimAndDot() { + mIsScrimShowing = false; mSensorPaint.setStyle(Paint.Style.STROKE); - postInvalidate(); + invalidate(); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index c5639ea6fcde..9e9d85a7cd1c 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -84,7 +84,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dumpable; -import com.android.systemui.bubbles.animation.StackAnimationController; import com.android.systemui.bubbles.dagger.BubbleModule; import com.android.systemui.dump.DumpManager; import com.android.systemui.model.SysUiState; @@ -96,6 +95,7 @@ import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationRemoveInterceptor; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.NotificationChannelHelper; import com.android.systemui.statusbar.notification.NotificationEntryListener; @@ -109,7 +109,6 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.phone.NotificationGroupManager; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; @@ -139,7 +138,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED, DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE, DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT, - DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED}) + DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED, + DISMISS_NO_BUBBLE_UP}) @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) @interface DismissReason {} @@ -156,6 +156,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi static final int DISMISS_OVERFLOW_MAX_REACHED = 11; static final int DISMISS_SHORTCUT_REMOVED = 12; static final int DISMISS_PACKAGE_REMOVED = 13; + static final int DISMISS_NO_BUBBLE_UP = 14; private final Context mContext; private final NotificationEntryManager mNotificationEntryManager; @@ -1262,8 +1263,18 @@ public class BubbleController implements ConfigurationController.ConfigurationLi rankingMap.getRanking(key, mTmpRanking); boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key); if (isActiveBubble && !mTmpRanking.canBubble()) { - mBubbleData.dismissBubbleWithKey(entry.getKey(), - BubbleController.DISMISS_BLOCKED); + // If this entry is no longer allowed to bubble, dismiss with the BLOCKED reason. + // This means that the app or channel's ability to bubble has been revoked. + mBubbleData.dismissBubbleWithKey( + key, BubbleController.DISMISS_BLOCKED); + } else if (isActiveBubble + && !mNotificationInterruptStateProvider.shouldBubbleUp(entry)) { + // If this entry is allowed to bubble, but cannot currently bubble up, dismiss it. + // This happens when DND is enabled and configured to hide bubbles. Dismissing with + // the reason DISMISS_NO_BUBBLE_UP will retain the underlying notification, so that + // the bubble will be re-created if shouldBubbleUp returns true. + mBubbleData.dismissBubbleWithKey( + key, BubbleController.DISMISS_NO_BUBBLE_UP); } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) { entry.setFlagBubble(true); onEntryUpdated(entry); @@ -1340,8 +1351,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mStackView.removeBubble(bubble); } - // If the bubble is removed for user switching, leave the notification in place. - if (reason == DISMISS_USER_CHANGED) { + // Leave the notification in place if we're dismissing due to user switching, or + // because DND is suppressing the bubble. In both of those cases, we need to be able + // to restore the bubble from the notification later. + if (reason == DISMISS_USER_CHANGED || reason == DISMISS_NO_BUBBLE_UP) { continue; } if (reason == DISMISS_NOTIF_CANCEL) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index 10f4385ed443..5c6d16d4bbee 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -34,7 +34,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController.DismissReason; -import com.android.systemui.shared.system.SysUiStatsLog; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -52,12 +52,11 @@ import java.util.function.Consumer; import java.util.function.Predicate; import javax.inject.Inject; -import javax.inject.Singleton; /** * Keeps track of active bubbles. */ -@Singleton +@SysUISingleton public class BubbleData { private BubbleLoggerImpl mLogger = new BubbleLoggerImpl(); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt index db64a13f3df3..f129d3147032 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt @@ -18,25 +18,24 @@ package com.android.systemui.bubbles import android.annotation.SuppressLint import android.annotation.UserIdInt import android.content.pm.LauncherApps +import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER -import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED import android.os.UserHandle import android.util.Log import com.android.systemui.bubbles.storage.BubbleEntity import com.android.systemui.bubbles.storage.BubblePersistentRepository import com.android.systemui.bubbles.storage.BubbleVolatileRepository +import com.android.systemui.dagger.SysUISingleton import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.launch import kotlinx.coroutines.yield - import javax.inject.Inject -import javax.inject.Singleton -@Singleton +@SysUISingleton internal class BubbleDataRepository @Inject constructor( private val volatileRepository: BubbleVolatileRepository, private val persistentRepository: BubblePersistentRepository, diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java index 10d301d0fa93..eecc41c697b3 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java @@ -25,23 +25,22 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.bubbles.BubbleData; import com.android.systemui.bubbles.BubbleDataRepository; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.phone.NotificationGroupManager; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.FloatingContentCoordinator; -import javax.inject.Singleton; - import dagger.Module; import dagger.Provides; @@ -51,7 +50,7 @@ public interface BubbleModule { /** */ - @Singleton + @SysUISingleton @Provides static BubbleController newBubbleController( Context context, diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt index 5b4d8c71e5c0..f4479653d12e 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt @@ -18,13 +18,13 @@ package com.android.systemui.bubbles.storage import android.content.Context import android.util.AtomicFile import android.util.Log +import com.android.systemui.dagger.SysUISingleton import java.io.File import java.io.FileOutputStream import java.io.IOException import javax.inject.Inject -import javax.inject.Singleton -@Singleton +@SysUISingleton class BubblePersistentRepository @Inject constructor( context: Context ) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt index 894970f903ac..c6d57326357c 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt @@ -19,8 +19,8 @@ import android.content.pm.LauncherApps import android.os.UserHandle import com.android.internal.annotations.VisibleForTesting import com.android.systemui.bubbles.ShortcutKey +import com.android.systemui.dagger.SysUISingleton import javax.inject.Inject -import javax.inject.Singleton private const val CAPACITY = 16 @@ -28,7 +28,7 @@ private const val CAPACITY = 16 * BubbleVolatileRepository holds the most updated snapshot of list of bubbles for in-memory * manipulation. */ -@Singleton +@SysUISingleton class BubbleVolatileRepository @Inject constructor( private val launcherApps: LauncherApps ) { diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index f35322bd2a77..83b6df3e701b 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -31,6 +31,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dumpable; import com.android.systemui.classifier.brightline.BrightLineFalsingManager; import com.android.systemui.classifier.brightline.FalsingDataProvider; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.dock.DockManager; @@ -48,14 +49,13 @@ import java.io.PrintWriter; import java.util.concurrent.Executor; import javax.inject.Inject; -import javax.inject.Singleton; /** * Simple passthrough implementation of {@link FalsingManager} allowing plugins to swap in. * * {@link FalsingManagerImpl} is used when a Plugin is not loaded. */ -@Singleton +@SysUISingleton public class FalsingManagerProxy implements FalsingManager, Dumpable { private static final String PROXIMITY_SENSOR_TAG = "FalsingManager"; diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java index 3ca1f59fd793..5b33428ff5f1 100644 --- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java +++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java @@ -28,6 +28,7 @@ import com.android.internal.colorextraction.types.ExtractionType; import com.android.internal.colorextraction.types.Tonal; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.policy.ConfigurationController; import java.io.FileDescriptor; @@ -35,12 +36,11 @@ import java.io.PrintWriter; import java.util.Arrays; import javax.inject.Inject; -import javax.inject.Singleton; /** * ColorExtractor aware of wallpaper visibility */ -@Singleton +@SysUISingleton public class SysuiColorExtractor extends ColorExtractor implements Dumpable, ConfigurationController.ConfigurationListener { private static final String TAG = "SysuiColorExtractor"; diff --git a/packages/SystemUI/src/com/android/systemui/controls/CustomIconCache.kt b/packages/SystemUI/src/com/android/systemui/controls/CustomIconCache.kt index cca0f1653757..ed608499c601 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/CustomIconCache.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/CustomIconCache.kt @@ -19,8 +19,8 @@ package com.android.systemui.controls import android.content.ComponentName import android.graphics.drawable.Icon import androidx.annotation.GuardedBy +import com.android.systemui.dagger.SysUISingleton import javax.inject.Inject -import javax.inject.Singleton /** * Icon cache for custom icons sent with controls. @@ -28,7 +28,7 @@ import javax.inject.Singleton * It assumes that only one component can be current at the time, to minimize the number of icons * stored at a given time. */ -@Singleton +@SysUISingleton class CustomIconCache @Inject constructor() { private var currentComponent: ComponentName? = null diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt index aa3e193ddba2..658f46e3bb96 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt @@ -28,14 +28,14 @@ import android.service.controls.IControlsSubscription import android.service.controls.actions.ControlAction import android.util.Log import com.android.internal.annotations.VisibleForTesting +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.util.concurrency.DelayableExecutor import dagger.Lazy import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject -import javax.inject.Singleton -@Singleton +@SysUISingleton @VisibleForTesting open class ControlsBindingControllerImpl @Inject constructor( private val context: Context, diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index 40c8c6bfa9f7..495872f3433d 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -42,6 +42,7 @@ import com.android.systemui.controls.ControlStatus import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.ui.ControlsUiController +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager import com.android.systemui.globalactions.GlobalActionsDialog @@ -52,18 +53,17 @@ import java.util.Optional import java.util.concurrent.TimeUnit import java.util.function.Consumer import javax.inject.Inject -import javax.inject.Singleton -@Singleton +@SysUISingleton class ControlsControllerImpl @Inject constructor ( - private val context: Context, - @Background private val executor: DelayableExecutor, - private val uiController: ControlsUiController, - private val bindingController: ControlsBindingController, - private val listingController: ControlsListingController, - private val broadcastDispatcher: BroadcastDispatcher, - optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>, - dumpManager: DumpManager + private val context: Context, + @Background private val executor: DelayableExecutor, + private val uiController: ControlsUiController, + private val bindingController: ControlsBindingController, + private val listingController: ControlsListingController, + private val broadcastDispatcher: BroadcastDispatcher, + optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>, + dumpManager: DumpManager ) : Dumpable, ControlsController { companion object { diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt index 9a5b96078e95..38a82f8c9908 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt @@ -19,10 +19,10 @@ package com.android.systemui.controls.dagger import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.ui.ControlsUiController +import com.android.systemui.dagger.SysUISingleton import dagger.Lazy import java.util.Optional import javax.inject.Inject -import javax.inject.Singleton /** * Pseudo-component to inject into classes outside `com.android.systemui.controls`. @@ -30,7 +30,7 @@ import javax.inject.Singleton * If `featureEnabled` is false, all the optionals should be empty. The controllers will only be * instantiated if `featureEnabled` is true. */ -@Singleton +@SysUISingleton class ControlsComponent @Inject constructor( @ControlsFeatureEnabled private val featureEnabled: Boolean, private val lazyControlsController: Lazy<ControlsController>, diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt index 4760d291072e..fbdeb30d3911 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt @@ -33,13 +33,13 @@ import com.android.systemui.controls.ui.ControlActionCoordinator import com.android.systemui.controls.ui.ControlActionCoordinatorImpl import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.controls.ui.ControlsUiControllerImpl +import com.android.systemui.dagger.SysUISingleton import dagger.Binds import dagger.BindsOptionalOf import dagger.Module import dagger.Provides import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap -import javax.inject.Singleton /** * Module for injecting classes in `com.android.systemui.controls`- @@ -55,7 +55,7 @@ abstract class ControlsModule { companion object { @JvmStatic @Provides - @Singleton + @SysUISingleton @ControlsFeatureEnabled fun providesControlsFeatureEnabled(pm: PackageManager): Boolean { return pm.hasSystemFeature(PackageManager.FEATURE_CONTROLS) diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt index 1cd9712cd01b..0d4439fe8ccb 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt @@ -27,11 +27,11 @@ import com.android.internal.annotations.VisibleForTesting import com.android.settingslib.applications.ServiceListing import com.android.settingslib.widget.CandidateInfo import com.android.systemui.controls.ControlsServiceInfo +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import java.util.concurrent.Executor import java.util.concurrent.atomic.AtomicInteger import javax.inject.Inject -import javax.inject.Singleton private fun createServiceListing(context: Context): ServiceListing { return ServiceListing.Builder(context).apply { @@ -52,7 +52,7 @@ private fun createServiceListing(context: Context): ServiceListing { * * Has an intent-filter responding to [ControlsProviderService.CONTROLS_ACTION] * * Has the bind permission `android.permission.BIND_CONTROLS` */ -@Singleton +@SysUISingleton class ControlsListingControllerImpl @VisibleForTesting constructor( private val context: Context, @Background private val backgroundExecutor: Executor, diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt index e15380b42a78..ab8222547597 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt @@ -16,30 +16,29 @@ package com.android.systemui.controls.ui +import android.annotation.MainThread import android.app.Dialog import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ResolveInfo -import android.annotation.MainThread -import android.os.Vibrator import android.os.VibrationEffect +import android.os.Vibrator import android.service.controls.Control import android.service.controls.actions.BooleanAction import android.service.controls.actions.CommandAction import android.service.controls.actions.FloatAction import android.util.Log import android.view.HapticFeedbackConstants +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.globalactions.GlobalActionsComponent import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.DelayableExecutor - import javax.inject.Inject -import javax.inject.Singleton -@Singleton +@SysUISingleton class ControlActionCoordinatorImpl @Inject constructor( private val context: Context, private val bgExecutor: DelayableExecutor, diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 5f75c96be128..371031020b14 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -52,6 +52,7 @@ import com.android.systemui.controls.management.ControlsEditingActivity import com.android.systemui.controls.management.ControlsFavoritingActivity import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.management.ControlsProviderSelectorActivity +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.globalactions.GlobalActionsPopupMenu @@ -62,11 +63,10 @@ import dagger.Lazy import java.text.Collator import java.util.function.Consumer import javax.inject.Inject -import javax.inject.Singleton private data class ControlKey(val componentName: ComponentName, val controlId: String) -@Singleton +@SysUISingleton class ControlsUiControllerImpl @Inject constructor ( val controlsController: Lazy<ControlsController>, val context: Context, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java index f91d79576395..b41915bc2547 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java @@ -27,12 +27,11 @@ import java.util.Map; import javax.inject.Inject; import javax.inject.Provider; -import javax.inject.Singleton; /** * Used during Service and Activity instantiation to make them injectable. */ -@Singleton +@SysUISingleton public class ContextComponentResolver implements ContextComponentHelper { private final Map<Class<?>, Provider<Activity>> mActivityCreators; private final Map<Class<?>, Provider<Service>> mServiceCreators; diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 3d31070905d0..5a7772320139 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -31,6 +31,7 @@ import android.view.Choreographer; import android.view.IWindowManager; import android.view.LayoutInflater; import android.view.WindowManager; +import android.view.accessibility.AccessibilityManager; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; @@ -40,6 +41,8 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.Prefs; import com.android.systemui.accessibility.ModeSwitchesController; +import com.android.systemui.accessibility.SystemActions; +import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger; import com.android.systemui.dagger.qualifiers.Background; @@ -47,25 +50,37 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.AlwaysOnDisplayPolicy; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.NavigationBarController; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.PluginInitializerImpl; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.recents.OverviewProxyService; +import com.android.systemui.recents.Recents; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.shared.plugins.PluginManagerImpl; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.DevicePolicyManagerWrapper; +import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.NavigationBarController; +import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; +import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DataSaverController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.util.leak.LeakDetector; +import java.util.Optional; import java.util.concurrent.Executor; import javax.inject.Named; -import javax.inject.Singleton; +import dagger.Lazy; import dagger.Module; import dagger.Provides; @@ -80,8 +95,9 @@ import dagger.Provides; @Module(includes = {NightDisplayListenerModule.class}) public class DependencyProvider { - @Singleton + /** */ @Provides + @SysUISingleton @Named(TIME_TICK_HANDLER_NAME) public Handler provideTimeTickHandler() { HandlerThread thread = new HandlerThread("TimeTick"); @@ -108,14 +124,16 @@ public class DependencyProvider { return new Handler(); } - @Singleton + /** */ @Provides + @SysUISingleton public DataSaverController provideDataSaverController(NetworkController networkController) { return networkController.getDataSaverController(); } - @Singleton + /** */ @Provides + @SysUISingleton public DisplayMetrics provideDisplayMetrics(Context context, WindowManager windowManager) { DisplayMetrics displayMetrics = new DisplayMetrics(); context.getDisplay().getMetrics(displayMetrics); @@ -123,69 +141,116 @@ public class DependencyProvider { } /** */ - @Singleton @Provides + @SysUISingleton public INotificationManager provideINotificationManager() { return INotificationManager.Stub.asInterface( ServiceManager.getService(Context.NOTIFICATION_SERVICE)); } /** */ - @Singleton @Provides + @SysUISingleton public LayoutInflater providerLayoutInflater(Context context) { return LayoutInflater.from(context); } - @Singleton + /** */ @Provides + @SysUISingleton public LeakDetector provideLeakDetector() { return LeakDetector.create(); } - @Singleton + /** */ @Provides + @SysUISingleton public MetricsLogger provideMetricsLogger() { return new MetricsLogger(); } - @Singleton + /** */ @Provides + @SysUISingleton public PluginManager providePluginManager(Context context) { return new PluginManagerImpl(context, new PluginInitializerImpl()); } - @Singleton + /** */ @Provides + @SysUISingleton public NavigationBarController provideNavigationBarController(Context context, - @Main Handler mainHandler, CommandQueue commandQueue) { - return new NavigationBarController(context, mainHandler, commandQueue); + WindowManager windowManager, + Lazy<AssistManager> assistManagerLazy, + AccessibilityManager accessibilityManager, + AccessibilityManagerWrapper accessibilityManagerWrapper, + DeviceProvisionedController deviceProvisionedController, + MetricsLogger metricsLogger, + OverviewProxyService overviewProxyService, + NavigationModeController navigationModeController, + StatusBarStateController statusBarStateController, + SysUiState sysUiFlagsContainer, + BroadcastDispatcher broadcastDispatcher, + CommandQueue commandQueue, + Divider divider, + Optional<Recents> recentsOptional, + Lazy<StatusBar> statusBarLazy, + ShadeController shadeController, + NotificationRemoteInputManager notificationRemoteInputManager, + SystemActions systemActions, + @Main Handler mainHandler, + UiEventLogger uiEventLogger, + ConfigurationController configurationController) { + return new NavigationBarController(context, + windowManager, + assistManagerLazy, + accessibilityManager, + accessibilityManagerWrapper, + deviceProvisionedController, + metricsLogger, + overviewProxyService, + navigationModeController, + statusBarStateController, + sysUiFlagsContainer, + broadcastDispatcher, + commandQueue, + divider, + recentsOptional, + statusBarLazy, + shadeController, + notificationRemoteInputManager, + systemActions, + mainHandler, + uiEventLogger, + configurationController); } - @Singleton + /** */ @Provides + @SysUISingleton public ConfigurationController provideConfigurationController(Context context) { return new ConfigurationControllerImpl(context); } /** */ - @Singleton + @SysUISingleton @Provides public AutoHideController provideAutoHideController(Context context, @Main Handler mainHandler, IWindowManager iWindowManager) { return new AutoHideController(context, mainHandler, iWindowManager); } - @Singleton + /** */ @Provides + @SysUISingleton public ActivityManagerWrapper provideActivityManagerWrapper() { return ActivityManagerWrapper.getInstance(); } /** Provides and initializes the {#link BroadcastDispatcher} for SystemUI */ - @Singleton @Provides + @SysUISingleton public BroadcastDispatcher providesBroadcastDispatcher( Context context, @Background Looper backgroundLooper, @@ -199,8 +264,9 @@ public class DependencyProvider { return bD; } - @Singleton + /** */ @Provides + @SysUISingleton public DevicePolicyManagerWrapper provideDevicePolicyManagerWrapper() { return DevicePolicyManagerWrapper.getInstance(); } @@ -230,22 +296,28 @@ public class DependencyProvider { } /** */ - @Singleton @Provides + public SystemActions providesSystemActions(Context context) { + return new SystemActions(context); + } + + /** */ + @Provides + @SysUISingleton public Choreographer providesChoreographer() { return Choreographer.getInstance(); } /** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */ - @Singleton @Provides + @SysUISingleton static UiEventLogger provideUiEventLogger() { return new UiEventLoggerImpl(); } /** */ - @Singleton @Provides + @SysUISingleton public ModeSwitchesController providesModeSwitchesController(Context context) { return new ModeSwitchesController(context); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java new file mode 100644 index 000000000000..553655bf672c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.dagger; + +import android.content.Context; +import android.content.SharedPreferences; +import android.hardware.display.AmbientDisplayConfiguration; +import android.util.DisplayMetrics; +import android.view.Choreographer; + +import com.android.systemui.Prefs; +import com.android.systemui.dagger.qualifiers.Main; + +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; + +/** + * Supplies globally scoped instances. + * + * Providers in this module will be accessible to both WMComponent and SysUIComponent scoped + * classes. They are in here because they are either needed globally or are inherently universal + * to the application. + * + * Note that just because a class might be used by both WM and SysUI does not necessarily mean that + * it should got into this module. If WM and SysUI might need the class for different purposes + * or different semantics, it may make sense to ask them to supply their own. Something like + * threading and concurrency provide a good example. Both components need + * Threads/Handlers/Executors, but they need separate instances of them in many cases. + * + * Please use discretion when adding things to the global scope. + */ +@Module +public class GlobalModule { + /** */ + @Provides + @Main + public SharedPreferences provideSharePreferences(Context context) { + return Prefs.get(context); + } + + /** */ + @Provides + public AmbientDisplayConfiguration provideAmbientDisplayConfiguration(Context context) { + return new AmbientDisplayConfiguration(context); + } + + /** */ + @Provides + @Singleton + public Choreographer providesChoreographer() { + return Choreographer.getInstance(); + } + + /** */ + @Provides + @Singleton + public DisplayMetrics provideDisplayMetrics(Context context) { + DisplayMetrics displayMetrics = new DisplayMetrics(); + context.getDisplay().getMetrics(displayMetrics); + return displayMetrics; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java new file mode 100644 index 000000000000..3d7c8ad4c43e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2019 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 com.android.systemui.dagger; + +import android.content.Context; + +import javax.inject.Singleton; + +import dagger.BindsInstance; +import dagger.Component; + +/** + * Root component for Dagger injection. + */ +@Singleton +@Component(modules = { + SysUISubcomponentModule.class, + WMModule.class}) +public interface GlobalRootComponent { + + /** + * Builder for a GlobalRootComponent. + */ + @Component.Builder + interface Builder { + @BindsInstance + Builder context(Context context); + + GlobalRootComponent build(); + } + + /** + * Builder for a WMComponent. + */ + WMComponent.Builder getWMComponentBuilder(); + + /** + * Builder for a SysuiComponent. + */ + SysUIComponent.Builder getSysUIComponent(); +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 9fdbb6daca51..7281faf1a2c4 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -16,34 +16,24 @@ package com.android.systemui.dagger; -import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; - -import android.content.ContentProvider; -import android.content.Context; - import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.Dependency; import com.android.systemui.InitController; import com.android.systemui.SystemUIAppComponentFactory; import com.android.systemui.dump.DumpManager; -import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.KeyguardSliceProvider; import com.android.systemui.onehanded.dagger.OneHandedModule; import com.android.systemui.pip.phone.dagger.PipModule; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.InjectionInflationController; -import javax.inject.Named; -import javax.inject.Singleton; - -import dagger.BindsInstance; -import dagger.Component; +import dagger.Subcomponent; /** - * Root component for Dagger injection. + * Dagger Subcomponent for Core SysUI. */ -@Singleton -@Component(modules = { +@SysUISingleton +@Subcomponent(modules = { DefaultComponentBinder.class, DependencyProvider.class, DependencyBinder.class, @@ -53,70 +43,54 @@ import dagger.Component; SystemUIBinder.class, SystemUIModule.class, SystemUIDefaultModule.class}) -public interface SystemUIRootComponent { +public interface SysUIComponent { /** - * Builder for a SystemUIRootComponent. + * Builder for a SysUIComponent. */ - @Component.Builder + @Subcomponent.Builder interface Builder { - @BindsInstance - Builder context(Context context); - - SystemUIRootComponent build(); + SysUIComponent build(); } /** * Provides a BootCompleteCache. */ - @Singleton + @SysUISingleton BootCompleteCacheImpl provideBootCacheImpl(); /** * Creates a ContextComponentHelper. */ - @Singleton + @SysUISingleton ConfigurationController getConfigurationController(); /** * Creates a ContextComponentHelper. */ - @Singleton + @SysUISingleton ContextComponentHelper getContextComponentHelper(); /** * Main dependency providing module. */ - @Singleton + @SysUISingleton Dependency createDependency(); /** */ - @Singleton + @SysUISingleton DumpManager createDumpManager(); /** - * FragmentCreator generates all Fragments that need injection. - */ - @Singleton - FragmentService.FragmentCreator createFragmentCreator(); - - - /** * Creates a InitController. */ - @Singleton + @SysUISingleton InitController getInitController(); /** - * ViewCreator generates all Views that need injection. + * ViewInstanceCreator generates all Views that need injection. */ - InjectionInflationController.ViewCreator createViewCreator(); - - /** - * Whether notification long press is allowed. - */ - @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) - boolean allowNotificationLongPressName(); + InjectionInflationController.ViewInstanceCreator.Factory createViewInstanceCreatorFactory(); /** * Member injection into the supplied argument. @@ -126,10 +100,5 @@ public interface SystemUIRootComponent { /** * Member injection into the supplied argument. */ - void inject(ContentProvider contentProvider); - - /** - * Member injection into the supplied argument. - */ void inject(KeyguardSliceProvider keyguardSliceProvider); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUISingleton.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUISingleton.java new file mode 100644 index 000000000000..a4b33427cbda --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUISingleton.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.dagger; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Scope; + +/** + * Scope annotation for singleton items within the SysUIComponent. + */ +@Documented +@Retention(RUNTIME) +@Scope +public @interface SysUISingleton { +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUISubcomponentModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUISubcomponentModule.java new file mode 100644 index 000000000000..aacc6939cf74 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUISubcomponentModule.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.dagger; + +import dagger.Module; + +/** + * Dagger module for including the WMComponent. + */ +@Module(subcomponents = {SysUIComponent.class}) +public abstract class SysUISubcomponentModule { +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java index 251ce1304930..7fe9faf5258c 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java @@ -43,6 +43,7 @@ import android.hardware.display.ColorDisplayManager; import android.hardware.display.DisplayManager; import android.media.AudioManager; import android.media.MediaRouter2Manager; +import android.media.session.MediaSessionManager; import android.net.ConnectivityManager; import android.net.NetworkScoreManager; import android.net.wifi.WifiManager; @@ -71,8 +72,6 @@ import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.system.PackageManagerWrapper; -import javax.inject.Singleton; - import dagger.Module; import dagger.Provides; @@ -82,49 +81,49 @@ import dagger.Provides; @Module public class SystemServicesModule { @Provides - @Singleton + @SysUISingleton static AccessibilityManager provideAccessibilityManager(Context context) { return context.getSystemService(AccessibilityManager.class); } @Provides - @Singleton + @SysUISingleton static ActivityManager provideActivityManager(Context context) { return context.getSystemService(ActivityManager.class); } - @Singleton + @SysUISingleton @Provides static AlarmManager provideAlarmManager(Context context) { return context.getSystemService(AlarmManager.class); } @Provides - @Singleton + @SysUISingleton static AudioManager provideAudioManager(Context context) { return context.getSystemService(AudioManager.class); } @Provides - @Singleton + @SysUISingleton static ColorDisplayManager provideColorDisplayManager(Context context) { return context.getSystemService(ColorDisplayManager.class); } @Provides - @Singleton + @SysUISingleton static ConnectivityManager provideConnectivityManagager(Context context) { return context.getSystemService(ConnectivityManager.class); } @Provides - @Singleton + @SysUISingleton static ContentResolver provideContentResolver(Context context) { return context.getContentResolver(); } @Provides - @Singleton + @SysUISingleton static DevicePolicyManager provideDevicePolicyManager(Context context) { return context.getSystemService(DevicePolicyManager.class); } @@ -136,44 +135,44 @@ public class SystemServicesModule { } @Provides - @Singleton + @SysUISingleton static DisplayManager provideDisplayManager(Context context) { return context.getSystemService(DisplayManager.class); } - @Singleton + @SysUISingleton @Provides static IActivityManager provideIActivityManager() { return ActivityManager.getService(); } - @Singleton + @SysUISingleton @Provides static IActivityTaskManager provideIActivityTaskManager() { return ActivityTaskManager.getService(); } @Provides - @Singleton + @SysUISingleton static IBatteryStats provideIBatteryStats() { return IBatteryStats.Stub.asInterface( ServiceManager.getService(BatteryStats.SERVICE_NAME)); } @Provides - @Singleton + @SysUISingleton static IDreamManager provideIDreamManager() { return IDreamManager.Stub.asInterface( ServiceManager.checkService(DreamService.DREAM_SERVICE)); } @Provides - @Singleton + @SysUISingleton static IPackageManager provideIPackageManager() { return IPackageManager.Stub.asInterface(ServiceManager.getService("package")); } - @Singleton + @SysUISingleton @Provides static IStatusBarService provideIStatusBarService() { return IStatusBarService.Stub.asInterface( @@ -187,32 +186,32 @@ public class SystemServicesModule { ServiceManager.getService(Context.WALLPAPER_SERVICE)); } - @Singleton + @SysUISingleton @Provides static IWindowManager provideIWindowManager() { return WindowManagerGlobal.getWindowManagerService(); } - @Singleton + @SysUISingleton @Provides static KeyguardManager provideKeyguardManager(Context context) { return context.getSystemService(KeyguardManager.class); } - @Singleton + @SysUISingleton @Provides static LatencyTracker provideLatencyTracker(Context context) { return LatencyTracker.getInstance(context); } - @Singleton + @SysUISingleton @Provides static LauncherApps provideLauncherApps(Context context) { return context.getSystemService(LauncherApps.class); } @SuppressLint("MissingPermission") - @Singleton + @SysUISingleton @Provides @Nullable static LocalBluetoothManager provideLocalBluetoothController(Context context, @@ -226,31 +225,36 @@ public class SystemServicesModule { } @Provides - @Singleton + static MediaSessionManager provideMediaSessionManager(Context context) { + return context.getSystemService(MediaSessionManager.class); + } + + @Provides + @SysUISingleton static NetworkScoreManager provideNetworkScoreManager(Context context) { return context.getSystemService(NetworkScoreManager.class); } - @Singleton + @SysUISingleton @Provides static NotificationManager provideNotificationManager(Context context) { return context.getSystemService(NotificationManager.class); } - @Singleton + @SysUISingleton @Provides static PackageManager providePackageManager(Context context) { return context.getPackageManager(); } - @Singleton + @SysUISingleton @Provides static PackageManagerWrapper providePackageManagerWrapper() { return PackageManagerWrapper.getInstance(); } /** */ - @Singleton + @SysUISingleton @Provides static PowerManager providePowerManager(Context context) { return context.getSystemService(PowerManager.class); @@ -263,51 +267,51 @@ public class SystemServicesModule { } @Provides - @Singleton + @SysUISingleton static SensorManager providesSensorManager(Context context) { return context.getSystemService(SensorManager.class); } - @Singleton + @SysUISingleton @Provides static SensorPrivacyManager provideSensorPrivacyManager(Context context) { return context.getSystemService(SensorPrivacyManager.class); } - @Singleton + @SysUISingleton @Provides static ShortcutManager provideShortcutManager(Context context) { return context.getSystemService(ShortcutManager.class); } @Provides - @Singleton + @SysUISingleton @Nullable static TelecomManager provideTelecomManager(Context context) { return context.getSystemService(TelecomManager.class); } @Provides - @Singleton + @SysUISingleton static TelephonyManager provideTelephonyManager(Context context) { return context.getSystemService(TelephonyManager.class); } @Provides - @Singleton + @SysUISingleton static TrustManager provideTrustManager(Context context) { return context.getSystemService(TrustManager.class); } @Provides - @Singleton + @SysUISingleton @Nullable static Vibrator provideVibrator(Context context) { return context.getSystemService(Vibrator.class); } @Provides - @Singleton + @SysUISingleton static UserManager provideUserManager(Context context) { return context.getSystemService(UserManager.class); } @@ -318,20 +322,20 @@ public class SystemServicesModule { } @Provides - @Singleton + @SysUISingleton @Nullable static WifiManager provideWifiManager(Context context) { return context.getSystemService(WifiManager.class); } - @Singleton + @SysUISingleton @Provides static WindowManager provideWindowManager(Context context) { return context.getSystemService(WindowManager.class); } @Provides - @Singleton + @SysUISingleton static RoleManager provideRoleManager(Context context) { return context.getSystemService(RoleManager.class); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java index 803e56db8ff3..3b225d5313c1 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java @@ -29,6 +29,7 @@ import com.android.keyguard.KeyguardViewController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.doze.DozeHost; @@ -44,12 +45,14 @@ import com.android.systemui.stackdivider.DividerModule; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.phone.DozeServiceHost; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl; import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.ShadeControllerImpl; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -59,10 +62,9 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.wmshell.WindowManagerShellModule; +import com.android.systemui.wmshell.WMShellModule; import javax.inject.Named; -import javax.inject.Singleton; import dagger.Binds; import dagger.Module; @@ -72,10 +74,14 @@ import dagger.Provides; * A dagger module for injecting default implementations of components of System UI that may be * overridden by the System UI implementation. */ -@Module(includes = {DividerModule.class, QSModule.class, WindowManagerShellModule.class}) +@Module(includes = { + DividerModule.class, + QSModule.class, + WMShellModule.class + }) public abstract class SystemUIDefaultModule { - @Singleton + @SysUISingleton @Provides @Named(LEAK_REPORT_EMAIL_NAME) @Nullable @@ -91,19 +97,29 @@ public abstract class SystemUIDefaultModule { NotificationLockscreenUserManagerImpl notificationLockscreenUserManager); @Provides - @Singleton - static BatteryController provideBatteryController(Context context, - EnhancedEstimates enhancedEstimates, PowerManager powerManager, - BroadcastDispatcher broadcastDispatcher, @Main Handler mainHandler, + @SysUISingleton + static BatteryController provideBatteryController( + Context context, + EnhancedEstimates enhancedEstimates, + PowerManager powerManager, + BroadcastDispatcher broadcastDispatcher, + DemoModeController demoModeController, + @Main Handler mainHandler, @Background Handler bgHandler) { - BatteryController bC = new BatteryControllerImpl(context, enhancedEstimates, powerManager, - broadcastDispatcher, mainHandler, bgHandler); + BatteryController bC = new BatteryControllerImpl( + context, + enhancedEstimates, + powerManager, + broadcastDispatcher, + demoModeController, + mainHandler, + bgHandler); bC.init(); return bC; } @Binds - @Singleton + @SysUISingleton public abstract QSFactory bindQSFactory(QSFactoryImpl qsFactoryImpl); @Binds @@ -116,14 +132,14 @@ public abstract class SystemUIDefaultModule { @Binds abstract ShadeController provideShadeController(ShadeControllerImpl shadeController); - @Singleton + @SysUISingleton @Provides @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) static boolean provideAllowNotificationLongPress() { return true; } - @Singleton + @SysUISingleton @Provides static HeadsUpManagerPhone provideHeadsUpManagerPhone( Context context, @@ -139,7 +155,7 @@ public abstract class SystemUIDefaultModule { abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone); @Provides - @Singleton + @SysUISingleton static Recents provideRecents(Context context, RecentsImplementation recentsImplementation, CommandQueue commandQueue) { return new Recents(context, recentsImplementation, commandQueue); @@ -154,5 +170,9 @@ public abstract class SystemUIDefaultModule { StatusBarKeyguardViewManager statusBarKeyguardViewManager); @Binds + abstract NotificationShadeWindowController bindNotificationShadeController( + NotificationShadeWindowControllerImpl notificationShadeWindowController); + + @Binds abstract DozeHost provideDozeHost(DozeServiceHost dozeServiceHost); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index f774358b69fb..abfc2ab1c6e8 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -24,8 +24,10 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.BootCompleteCache; import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.assist.AssistModule; +import com.android.systemui.demomode.dagger.DemoModeModule; import com.android.systemui.doze.dagger.DozeComponent; import com.android.systemui.dump.DumpManager; +import com.android.systemui.fragments.FragmentService; import com.android.systemui.log.dagger.LogModule; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -50,8 +52,6 @@ import com.android.systemui.util.settings.SettingsUtilModule; import com.android.systemui.util.time.SystemClock; import com.android.systemui.util.time.SystemClockImpl; -import javax.inject.Singleton; - import dagger.Binds; import dagger.BindsOptionalOf; import dagger.Module; @@ -64,6 +64,7 @@ import dagger.Provides; @Module(includes = { AssistModule.class, ConcurrencyModule.class, + DemoModeModule.class, LogModule.class, PeopleHubModule.class, SensorModule.class, @@ -74,7 +75,8 @@ import dagger.Provides; NotificationRowComponent.class, DozeComponent.class, ExpandableNotificationRowComponent.class, - NotificationShelfComponent.class}) + NotificationShelfComponent.class, + FragmentService.FragmentCreator.class}) public abstract class SystemUIModule { @Binds @@ -85,7 +87,7 @@ public abstract class SystemUIModule { public abstract ContextComponentHelper bindComponentHelper( ContextComponentResolver componentHelper); - @Singleton + @SysUISingleton @Provides @Nullable static KeyguardLiftController provideKeyguardLiftController( @@ -106,7 +108,7 @@ public abstract class SystemUIModule { public abstract NotificationRowBinder bindNotificationRowBinder( NotificationRowBinderImpl notificationRowBinder); - @Singleton + @SysUISingleton @Provides static SysUiState provideSysUiState() { return new SysUiState(); @@ -127,7 +129,7 @@ public abstract class SystemUIModule { @BindsOptionalOf abstract StatusBar optionalStatusBar(); - @Singleton + @SysUISingleton @Binds abstract SystemClock bindSystemClock(SystemClockImpl systemClock); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java new file mode 100644 index 000000000000..929b61a3421c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2019 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 com.android.systemui.dagger; + +import dagger.Subcomponent; + +/** + * Dagger Subcomponent for WindowManager. + */ +@WMSingleton +@Subcomponent(modules = {}) +public interface WMComponent { + + /** + * Builder for a WMComponent. + */ + @Subcomponent.Builder + interface Builder { + WMComponent build(); + } +} diff --git a/tests/AutoVerify/app2/src/com/android/test/autoverify/MainActivity.java b/packages/SystemUI/src/com/android/systemui/dagger/WMModule.java index 09ef47212622..2894780e04b8 100644 --- a/tests/AutoVerify/app2/src/com/android/test/autoverify/MainActivity.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMModule.java @@ -13,3 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +package com.android.systemui.dagger; + +import dagger.Module; + +/** + * Dagger module for including the WMComponent. + */ +@Module(subcomponents = {WMComponent.class}) +public abstract class WMModule { +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java b/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java new file mode 100644 index 000000000000..7292b9e459e0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.dagger; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Scope; + +/** + * Scope annotation for singleton items within the WMComponent. + */ +@Documented +@Retention(RUNTIME) +@Scope +public @interface WMSingleton { +} diff --git a/packages/SystemUI/src/com/android/systemui/demomode/DemoMode.java b/packages/SystemUI/src/com/android/systemui/demomode/DemoMode.java new file mode 100644 index 000000000000..672ade2655a3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/demomode/DemoMode.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.demomode; + +import com.google.android.collect.Lists; + +import java.util.ArrayList; +import java.util.List; + +/** + * Interface defining what it means to implement DemoMode. A DemoMode implementation should + * register with DemoModeController, providing a list of commands for wish to listen. + * + * If you only need to listen to commands, but *do not* care about demo mode state changes, you + * can implement DemoModeCommandReceiver instead + */ +public interface DemoMode extends DemoModeCommandReceiver { + + List<String> NO_COMMANDS = new ArrayList<>(); + + /** Provide a set of commands to listen to, only acceptable values are the COMMAND_* keys */ + default List<String> demoCommands() { + return NO_COMMANDS; + } + + String ACTION_DEMO = "com.android.systemui.demo"; + + String EXTRA_COMMAND = "command"; + + /** Enter and exit are non-register-able; override started/finished to observe these states */ + String COMMAND_ENTER = "enter"; + String COMMAND_EXIT = "exit"; + + /** Observable commands to register a listener for */ + String COMMAND_CLOCK = "clock"; + String COMMAND_BATTERY = "battery"; + String COMMAND_NETWORK = "network"; + String COMMAND_BARS = "bars"; + String COMMAND_STATUS = "status"; + String COMMAND_NOTIFICATIONS = "notifications"; + String COMMAND_VOLUME = "volume"; + String COMMAND_OPERATOR = "operator"; + + /** New keys need to be added here */ + List<String> COMMANDS = Lists.newArrayList( + COMMAND_BARS, + COMMAND_BATTERY, + COMMAND_CLOCK, + COMMAND_NETWORK, + COMMAND_NOTIFICATIONS, + COMMAND_OPERATOR, + COMMAND_STATUS, + COMMAND_VOLUME + ); +} + diff --git a/packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt b/packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt new file mode 100644 index 000000000000..16f415070b45 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.demomode + +import android.content.Context +import android.database.ContentObserver +import android.os.Handler +import android.os.Looper +import android.provider.Settings + +/** + * Class to track the availability of [DemoMode]. Use this class to track the availability and + * on/off state for DemoMode + * + * This class works by wrapping a content observer for the relevant keys related to DemoMode + * availability and current on/off state, and triggering callbacks. + */ +abstract class DemoModeAvailabilityTracker(val context: Context) { + var isInDemoMode = false + var isDemoModeAvailable = false + + init { + isInDemoMode = checkIsDemoModeOn() + isDemoModeAvailable = checkIsDemoModeAllowed() + } + + fun startTracking() { + val resolver = context.contentResolver + resolver.registerContentObserver( + Settings.Global.getUriFor(DEMO_MODE_ALLOWED), false, allowedObserver) + resolver.registerContentObserver( + Settings.Global.getUriFor(DEMO_MODE_ON), false, onObserver) + } + + fun stopTracking() { + val resolver = context.contentResolver + resolver.unregisterContentObserver(allowedObserver) + resolver.unregisterContentObserver(onObserver) + } + + abstract fun onDemoModeAvailabilityChanged() + abstract fun onDemoModeStarted() + abstract fun onDemoModeFinished() + + private fun checkIsDemoModeAllowed(): Boolean { + return Settings.Global + .getInt(context.contentResolver, DEMO_MODE_ALLOWED, 0) != 0 + } + + private fun checkIsDemoModeOn(): Boolean { + return Settings.Global.getInt(context.contentResolver, DEMO_MODE_ON, 0) != 0 + } + + private val allowedObserver = object : ContentObserver(Handler(Looper.getMainLooper())) { + override fun onChange(selfChange: Boolean) { + val allowed = checkIsDemoModeAllowed() + if (DEBUG) { + android.util.Log.d(TAG, "onChange: DEMO_MODE_ALLOWED changed: $allowed") + } + + if (isDemoModeAvailable == allowed) { + return + } + + isDemoModeAvailable = allowed + onDemoModeAvailabilityChanged() + } + } + + private val onObserver = object : ContentObserver(Handler(Looper.getMainLooper())) { + override fun onChange(selfChange: Boolean) { + val on = checkIsDemoModeOn() + + if (DEBUG) { + android.util.Log.d(TAG, "onChange: DEMO_MODE_ON changed: $on") + } + + if (isInDemoMode == on) { + return + } + + isInDemoMode = on + if (on) { + onDemoModeStarted() + } else { + onDemoModeFinished() + } + } + } +} + +private const val TAG = "DemoModeAvailabilityTracker" +private const val DEMO_MODE_ALLOWED = "sysui_demo_allowed" +private const val DEMO_MODE_ON = "sysui_tuner_demo_on" +private const val DEBUG = false diff --git a/packages/SystemUI/src/com/android/systemui/demomode/DemoModeCommandReceiver.java b/packages/SystemUI/src/com/android/systemui/demomode/DemoModeCommandReceiver.java new file mode 100644 index 000000000000..609536a4b8e8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/demomode/DemoModeCommandReceiver.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.demomode; + +import android.os.Bundle; + +/** Defines the basic DemoMode command interface */ +public interface DemoModeCommandReceiver { + /** Override point to observe demo mode commands */ + void dispatchDemoCommand(String command, Bundle args); + + /** + * Demo mode starts due to receiving [COMMAND_ENTER] or receiving any other demo mode command + * while [DEMO_MODE_ALLOWED] is true but demo mode is off + */ + default void onDemoModeStarted() {} + + /** Demo mode exited */ + default void onDemoModeFinished() {} + +} diff --git a/packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt b/packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt new file mode 100644 index 000000000000..c76f55631ac9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.demomode + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.Bundle +import android.os.UserHandle +import android.util.Log +import com.android.systemui.Dumpable +import com.android.systemui.demomode.DemoMode.ACTION_DEMO +import com.android.systemui.dump.DumpManager +import com.android.systemui.statusbar.policy.CallbackController +import com.android.systemui.util.Assert +import com.android.systemui.util.settings.GlobalSettings +import java.io.FileDescriptor +import java.io.PrintWriter + +/** + * Handles system broadcasts for [DemoMode] + * + * Injected via [DemoModeModule] + */ +class DemoModeController constructor( + private val context: Context, + private val dumpManager: DumpManager, + private val globalSettings: GlobalSettings +) : CallbackController<DemoMode>, Dumpable { + + // Var updated when the availability tracker changes, or when we enter/exit demo mode in-process + var isInDemoMode = false + + var isAvailable = false + get() = tracker.isDemoModeAvailable + + private var initialized = false + + private val receivers = mutableListOf<DemoMode>() + private val receiverMap: Map<String, MutableList<DemoMode>> + + init { + val m = mutableMapOf<String, MutableList<DemoMode>>() + DemoMode.COMMANDS.map { command -> + m.put(command, mutableListOf()) + } + receiverMap = m + } + + fun initialize() { + if (initialized) { + throw IllegalStateException("Already initialized") + } + + initialized = true + + dumpManager.registerDumpable(TAG, this) + + // Due to DemoModeFragment running in systemui:tuner process, we have to observe for + // content changes to know if the setting turned on or off + tracker.startTracking() + + // TODO: We should probably exit demo mode if we booted up with it on + isInDemoMode = tracker.isInDemoMode + + val demoFilter = IntentFilter() + demoFilter.addAction(ACTION_DEMO) + context.registerReceiverAsUser(broadcastReceiver, UserHandle.ALL, demoFilter, + android.Manifest.permission.DUMP, null) + } + + override fun addCallback(listener: DemoMode) { + Assert.isMainThread() + + // Register this listener for its commands + val commands = listener.demoCommands() + + commands.forEach { command -> + if (!receiverMap.containsKey(command)) { + throw IllegalStateException("Command ($command) not recognized. " + + "See DemoMode.java for valid commands") + } + + receiverMap[command]!!.add(listener) + } + + receivers.add(listener) + if (isInDemoMode) { + listener.onDemoModeStarted() + } + } + + override fun removeCallback(listener: DemoMode) { + Assert.isMainThread() + + listener.demoCommands().forEach { command -> + receiverMap[command]!!.remove(listener) + } + + receivers.remove(listener) + } + + private fun setIsDemoModeAllowed(enabled: Boolean) { + // Turn off demo mode if it was on + if (isInDemoMode && !enabled) { + requestFinishDemoMode() + } + } + + private fun enterDemoMode() { + isInDemoMode = true + Assert.isMainThread() + receivers.forEach { r -> + r.onDemoModeStarted() + } + } + + private fun exitDemoMode() { + isInDemoMode = false + Assert.isMainThread() + receivers.forEach { r -> + r.onDemoModeFinished() + } + } + + fun dispatchDemoCommand(command: String, args: Bundle) { + Assert.isMainThread() + + if (DEBUG) { + Log.d(TAG, "dispatchDemoCommand: $command, args=$args") + } + + if (!isAvailable) { + return + } + + if (command == DemoMode.COMMAND_ENTER) { + enterDemoMode() + } else if (command == DemoMode.COMMAND_EXIT) { + exitDemoMode() + } else if (!isInDemoMode) { + enterDemoMode() + } + + // See? demo mode is easy now, you just notify the listeners when their command is called + receiverMap[command]!!.forEach { receiver -> + receiver.dispatchDemoCommand(command, args) + } + } + + override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { + pw.println("DemoModeController state -") + pw.println(" isInDemoMode=$isInDemoMode") + pw.println(" isDemoModeAllowed=$isAvailable") + pw.print(" receivers=[") + receivers.forEach { recv -> + pw.print(" ${recv.javaClass.simpleName}") + } + pw.println(" ]") + pw.println(" receiverMap= [") + receiverMap.keys.forEach { command -> + pw.print(" $command : [") + val recvs = receiverMap[command]!!.map { receiver -> + receiver.javaClass.simpleName + }.joinToString(",") + pw.println("$recvs ]") + } + } + + private val tracker = object : DemoModeAvailabilityTracker(context) { + override fun onDemoModeAvailabilityChanged() { + setIsDemoModeAllowed(isDemoModeAvailable) + } + + override fun onDemoModeStarted() { + if (this@DemoModeController.isInDemoMode != isInDemoMode) { + enterDemoMode() + } + } + + override fun onDemoModeFinished() { + if (this@DemoModeController.isInDemoMode != isInDemoMode) { + exitDemoMode() + } + } + } + + private val broadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (DEBUG) { + Log.v(TAG, "onReceive: $intent") + } + + val action = intent.action + if (!ACTION_DEMO.equals(action)) { + return + } + + val bundle = intent.extras ?: return + val command = bundle.getString("command", "").trim().toLowerCase() + if (command.length == 0) { + return + } + + try { + dispatchDemoCommand(command, bundle) + } catch (t: Throwable) { + Log.w(TAG, "Error running demo command, intent=$intent $t") + } + } + } + + fun requestSetDemoModeAllowed(allowed: Boolean) { + setGlobal(DEMO_MODE_ALLOWED, if (allowed) 1 else 0) + } + + fun requestStartDemoMode() { + setGlobal(DEMO_MODE_ON, 1) + } + + fun requestFinishDemoMode() { + setGlobal(DEMO_MODE_ON, 0) + } + + private fun setGlobal(key: String, value: Int) { + globalSettings.putInt(key, value) + } +} + +private const val TAG = "DemoModeController" +private const val DEMO_MODE_ALLOWED = "sysui_demo_allowed" +private const val DEMO_MODE_ON = "sysui_tuner_demo_on" + +private const val DEBUG = false diff --git a/packages/SystemUI/src/com/android/systemui/demomode/dagger/DemoModeModule.java b/packages/SystemUI/src/com/android/systemui/demomode/dagger/DemoModeModule.java new file mode 100644 index 000000000000..de9affb5c748 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/demomode/dagger/DemoModeModule.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.demomode.dagger; + +import android.content.Context; + +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.demomode.DemoModeController; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.util.settings.GlobalSettings; + +import dagger.Module; +import dagger.Provides; + +/** + * Dagger module providing {@link DemoModeController} + */ +@Module +public abstract class DemoModeModule { + /** Provides DemoModeController */ + @Provides + @SysUISingleton + static DemoModeController provideDemoModeController( + Context context, + DumpManager dumpManager, + GlobalSettings globalSettings) { + DemoModeController dmc = new DemoModeController(context, dumpManager, globalSettings); + dmc.initialize(); + return /*run*/dmc; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dock/DockManagerImpl.java b/packages/SystemUI/src/com/android/systemui/dock/DockManagerImpl.java index efc39058be39..8f399754730b 100644 --- a/packages/SystemUI/src/com/android/systemui/dock/DockManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/dock/DockManagerImpl.java @@ -16,10 +16,11 @@ package com.android.systemui.dock; +import com.android.systemui.dagger.SysUISingleton; + import javax.inject.Inject; -import javax.inject.Singleton; -@Singleton +@SysUISingleton public class DockManagerImpl implements DockManager { @Inject diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java index a311a450c6b7..99d2651ae9ea 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java @@ -24,6 +24,7 @@ import androidx.annotation.NonNull; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import java.io.FileDescriptor; @@ -32,14 +33,13 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import javax.inject.Inject; -import javax.inject.Singleton; /** * Logs doze events for debugging and triaging purposes. Logs are dumped in bugreports or on demand: * adb shell dumpsys activity service com.android.systemui/.SystemUIService \ * dependency DumpController DozeLog,DozeStats */ -@Singleton +@SysUISingleton public class DozeLog implements Dumpable { private final DozeLogger mLogger; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index 19b0ea1db04e..1a1cc072c6bf 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -33,6 +33,9 @@ import java.io.PrintWriter; import javax.inject.Inject; +import dagger.Reusable; + +@Reusable // Don't create multiple DozeServices. public class DozeService extends DreamService implements DozeMachine.Service, RequestDoze, PluginListener<DozeServicePlugin> { private static final String TAG = "DozeService"; @@ -57,7 +60,7 @@ public class DozeService extends DreamService setWindowless(true); mPluginManager.addPluginListener(this, DozeServicePlugin.class, false /* allowMultiple */); - DozeComponent dozeComponent = mDozeComponentBuilder.build(this); + DozeComponent dozeComponent = mDozeComponentBuilder.build(); mDozeMachine = dozeComponent.getDozeMachine(); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java index e925927e7564..247285434df9 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java @@ -19,7 +19,6 @@ package com.android.systemui.doze.dagger; import com.android.systemui.doze.DozeMachine; import com.android.systemui.doze.DozeService; -import dagger.BindsInstance; import dagger.Subcomponent; /** @@ -31,7 +30,7 @@ public interface DozeComponent { /** Simple Builder for {@link DozeComponent}. */ @Subcomponent.Factory interface Builder { - DozeComponent build(@BindsInstance DozeService dozeService); + DozeComponent build(); } /** Supply a {@link DozeMachine}. */ diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt index bbb77504ec27..bfa478088cad 100644 --- a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt +++ b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt @@ -18,11 +18,11 @@ package com.android.systemui.dump import android.util.ArrayMap import com.android.systemui.Dumpable +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.LogBuffer import java.io.FileDescriptor import java.io.PrintWriter import javax.inject.Inject -import javax.inject.Singleton /** * Maintains a registry of things that should be dumped when a bug report is taken @@ -33,7 +33,7 @@ import javax.inject.Singleton * * See [DumpHandler] for more information on how and when this information is dumped. */ -@Singleton +@SysUISingleton class DumpManager @Inject constructor() { private val dumpables: MutableMap<String, RegisteredDumpable<Dumpable>> = ArrayMap() private val buffers: MutableMap<String, RegisteredDumpable<LogBuffer>> = ArrayMap() diff --git a/packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt b/packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt index 603cb672175d..0eab1afc4119 100644 --- a/packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt +++ b/packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt @@ -18,6 +18,7 @@ package com.android.systemui.dump import android.content.Context import android.util.Log +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.LogBuffer import com.android.systemui.util.io.Files import com.android.systemui.util.time.SystemClock @@ -33,7 +34,6 @@ import java.text.SimpleDateFormat import java.util.Locale import java.util.concurrent.TimeUnit import javax.inject.Inject -import javax.inject.Singleton /** * Dumps all [LogBuffer]s to a file @@ -41,7 +41,7 @@ import javax.inject.Singleton * Intended for emergencies, i.e. we're about to crash. This file can then be read at a later date * (usually in a bug report). */ -@Singleton +@SysUISingleton class LogBufferEulogizer( private val dumpManager: DumpManager, private val systemClock: SystemClock, diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java index ae200763b3b0..0673879758ad 100644 --- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java +++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java @@ -21,9 +21,8 @@ import android.util.ArrayMap; import android.view.View; import com.android.systemui.Dumpable; -import com.android.systemui.dagger.SystemUIRootComponent; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.qs.QSFragment; -import com.android.systemui.statusbar.phone.NavigationBarFragment; import com.android.systemui.statusbar.policy.ConfigurationController; import java.io.FileDescriptor; @@ -32,7 +31,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Subcomponent; @@ -40,7 +38,7 @@ import dagger.Subcomponent; * Holds a map of root views to FragmentHostStates and generates them as needed. * Also dispatches the configuration changes to all current FragmentHostStates. */ -@Singleton +@SysUISingleton public class FragmentService implements Dumpable { private static final String TAG = "FragmentService"; @@ -61,9 +59,9 @@ public class FragmentService implements Dumpable { }; @Inject - public FragmentService(SystemUIRootComponent rootComponent, + public FragmentService(FragmentCreator.Factory fragmentCreatorFactory, ConfigurationController configurationController) { - mFragmentCreator = rootComponent.createFragmentCreator(); + mFragmentCreator = fragmentCreatorFactory.build(); initInjectionMap(); configurationController.addCallback(mConfigurationListener); } @@ -121,10 +119,11 @@ public class FragmentService implements Dumpable { */ @Subcomponent public interface FragmentCreator { - /** - * Inject a NavigationBarFragment. - */ - NavigationBarFragment createNavigationBarFragment(); + /** Factory for creating a FragmentCreator. */ + @Subcomponent.Factory + interface Factory { + FragmentCreator build(); + } /** * Inject a QSFragment. */ diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java index b29c5b07c765..86c8565bf8ef 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java @@ -20,6 +20,7 @@ import android.os.ServiceManager; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.GlobalActions; import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.statusbar.CommandQueue; @@ -30,12 +31,11 @@ import com.android.systemui.statusbar.policy.ExtensionController.Extension; import javax.inject.Inject; import javax.inject.Provider; -import javax.inject.Singleton; /** * Manages power menu plugins and communicates power menu actions to the StatusBar. */ -@Singleton +@SysUISingleton public class GlobalActionsComponent extends SystemUI implements Callbacks, GlobalActionsManager { private final CommandQueue mCommandQueue; diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index ef51abb1404d..d213ac1132bb 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -132,7 +132,7 @@ import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.settings.CurrentUserContextTracker; import com.android.systemui.statusbar.NotificationShadeDepthController; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.EmergencyDialerConstants; @@ -148,6 +148,7 @@ import java.util.Set; import java.util.concurrent.Executor; import javax.inject.Inject; +import javax.inject.Provider; /** * Helper to show the global actions dialog. Each item is an {@link Action} that may show depending @@ -401,7 +402,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, if (mDialog != null) { if (!mDialog.isShowingControls() && shouldShowControls()) { mDialog.showControls(mControlsUiControllerOptional.get()); - } else if (shouldShowLockMessage()) { + } else if (shouldShowLockMessage(mDialog)) { mDialog.showLockMessage(); } } @@ -698,19 +699,17 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mPowerAdapter = new MyPowerOptionsAdapter(); mDepthController.setShowingHomeControls(true); - GlobalActionsPanelPlugin.PanelViewController walletViewController = - getWalletViewController(); ControlsUiController uiController = null; if (mControlsUiControllerOptional.isPresent() && shouldShowControls()) { uiController = mControlsUiControllerOptional.get(); } ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, mOverflowAdapter, - walletViewController, mDepthController, mSysuiColorExtractor, + this::getWalletViewController, mDepthController, mSysuiColorExtractor, mStatusBarService, mNotificationShadeWindowController, controlsAvailable(), uiController, mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter); - if (shouldShowLockMessage()) { + if (shouldShowLockMessage(dialog)) { dialog.showLockMessage(); } dialog.setCanceledOnTouchOutside(false); // Handled by the custom class. @@ -2124,7 +2123,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private MultiListLayout mGlobalActionsLayout; private Drawable mBackgroundDrawable; private final SysuiColorExtractor mColorExtractor; - private final GlobalActionsPanelPlugin.PanelViewController mWalletViewController; + private final Provider<GlobalActionsPanelPlugin.PanelViewController> mWalletFactory; + @Nullable private GlobalActionsPanelPlugin.PanelViewController mWalletViewController; private boolean mKeyguardShowing; private boolean mShowing; private float mScrimAlpha; @@ -2144,7 +2144,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private TextView mLockMessage; ActionsDialog(Context context, MyAdapter adapter, MyOverflowAdapter overflowAdapter, - GlobalActionsPanelPlugin.PanelViewController walletViewController, + Provider<GlobalActionsPanelPlugin.PanelViewController> walletFactory, NotificationShadeDepthController depthController, SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService, NotificationShadeWindowController notificationShadeWindowController, @@ -2165,6 +2165,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mSysUiState = sysuiState; mOnRotateCallback = onRotateCallback; mKeyguardShowing = keyguardShowing; + mWalletFactory = walletFactory; // Window initialization Window window = getWindow(); @@ -2187,7 +2188,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, window.getAttributes().setFitInsetsTypes(0 /* types */); setTitle(R.string.global_actions); - mWalletViewController = walletViewController; initializeLayout(); } @@ -2200,8 +2200,13 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mControlsUiController.show(mControlsView, this::dismissForControlsActivity); } + private boolean isWalletViewAvailable() { + return mWalletViewController != null && mWalletViewController.getPanelContent() != null; + } + private void initializeWalletView() { - if (mWalletViewController == null || mWalletViewController.getPanelContent() == null) { + mWalletViewController = mWalletFactory.get(); + if (!isWalletViewAvailable()) { return; } @@ -2507,6 +2512,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private void dismissWallet() { if (mWalletViewController != null) { mWalletViewController.onDismissed(); + // The wallet controller should not be re-used after being dismissed. + mWalletViewController = null; } } @@ -2648,18 +2655,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, && !mControlsServiceInfos.isEmpty(); } - private boolean walletViewAvailable() { - GlobalActionsPanelPlugin.PanelViewController walletViewController = - getWalletViewController(); - return walletViewController != null && walletViewController.getPanelContent() != null; - } - - private boolean shouldShowLockMessage() { + private boolean shouldShowLockMessage(ActionsDialog dialog) { boolean isLockedAfterBoot = mLockPatternUtils.getStrongAuthForUser(getCurrentUser().id) == STRONG_AUTH_REQUIRED_AFTER_BOOT; return !mKeyguardStateController.isUnlocked() && (!mShowLockScreenCardsAndControls || isLockedAfterBoot) - && (controlsAvailable() || walletViewAvailable()); + && (controlsAvailable() || dialog.isWalletViewAvailable()); } private void onPowerMenuLockScreenSettingsChanged() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java b/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java index f6f3b9985371..d1bbc3359917 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java @@ -17,18 +17,18 @@ package com.android.systemui.keyguard; import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.UiBackground; import java.util.ArrayList; import java.util.concurrent.Executor; import javax.inject.Inject; -import javax.inject.Singleton; /** * Registry holding the current set of {@link IKeyguardDismissCallback}s. */ -@Singleton +@SysUISingleton public class DismissCallbackRegistry { private final ArrayList<DismissCallbackWrapper> mDismissCallbacks = new ArrayList<>(); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java index 4a33590f64d2..f527775449bf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java @@ -19,13 +19,14 @@ package com.android.systemui.keyguard; import android.os.Handler; import android.os.Message; +import com.android.systemui.dagger.SysUISingleton; + import javax.inject.Inject; -import javax.inject.Singleton; /** * Dispatches the lifecycles keyguard gets from WindowManager on the main thread. */ -@Singleton +@SysUISingleton public class KeyguardLifecyclesDispatcher { static final int SCREEN_TURNING_ON = 0; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 3a37c0fd4634..daef2c506ad3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -52,7 +52,6 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SystemUIAppComponentFactory; -import com.android.systemui.SystemUIFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.StatusBarState; @@ -72,6 +71,9 @@ import javax.inject.Inject; /** * Simple Slice provider that shows the current date. + * + * Injection is handled by {@link SystemUIAppComponentFactory} + + * {@link com.android.systemui.dagger.GlobalRootComponent#inject(KeyguardSliceProvider)}. */ public class KeyguardSliceProvider extends SliceProvider implements NextAlarmController.NextAlarmChangeCallback, ZenModeController.Callback, @@ -298,7 +300,8 @@ public class KeyguardSliceProvider extends SliceProvider implements @Override public boolean onCreateSliceProvider() { mContextAvailableCallback.onContextAvailable(getContext()); - inject(); + mMediaWakeLock = new SettableWakeLock(WakeLock.createPartial(getContext(), "media"), + "media"); synchronized (KeyguardSliceProvider.sInstanceLock) { KeyguardSliceProvider oldInstance = KeyguardSliceProvider.sInstance; if (oldInstance != null) { @@ -319,13 +322,6 @@ public class KeyguardSliceProvider extends SliceProvider implements } @VisibleForTesting - protected void inject() { - SystemUIFactory.getInstance().getRootComponent().inject(this); - mMediaWakeLock = new SettableWakeLock(WakeLock.createPartial(getContext(), "media"), - "media"); - } - - @VisibleForTesting protected void onDestroy() { synchronized (KeyguardSliceProvider.sInstanceLock) { mNextAlarmController.removeCallback(this); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 75f4809d752f..6214a6448287 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -85,7 +85,6 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.SystemUI; -import com.android.systemui.SystemUIFactory; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.dump.DumpManager; @@ -94,7 +93,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NavigationModeController; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.DeviceConfigProxy; @@ -229,6 +228,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { /** TrustManager for letting it know when we change visibility */ private final TrustManager mTrustManager; + private final InjectionInflationController mInjectionInflationController; /** * Used to keep the device awake while to ensure the keyguard finishes opening before @@ -723,7 +723,8 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { @UiBackground Executor uiBgExecutor, PowerManager powerManager, TrustManager trustManager, DeviceConfigProxy deviceConfig, - NavigationModeController navigationModeController) { + NavigationModeController navigationModeController, + InjectionInflationController injectionInflationController) { super(context); mFalsingManager = falsingManager; mLockPatternUtils = lockPatternUtils; @@ -734,6 +735,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { mUpdateMonitor = keyguardUpdateMonitor; mPM = powerManager; mTrustManager = trustManager; + mInjectionInflationController = injectionInflationController; dumpManager.registerDumpable(getClass().getName(), this); mDeviceConfig = deviceConfig; mShowHomeOverLockscreen = mDeviceConfig.getBoolean( @@ -773,10 +775,8 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { mContext.registerReceiver(mDelayedLockBroadcastReceiver, delayedActionFilter, SYSTEMUI_PERMISSION, null /* scheduler */); - InjectionInflationController injectionInflationController = - new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()); mKeyguardDisplayManager = new KeyguardDisplayManager(mContext, - injectionInflationController); + mInjectionInflationController); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java index 834bca55103a..084e84a7a77a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java @@ -19,17 +19,17 @@ package com.android.systemui.keyguard; import android.os.Trace; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; import java.io.FileDescriptor; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Singleton; /** * Tracks the screen lifecycle. */ -@Singleton +@SysUISingleton public class ScreenLifecycle extends Lifecycle<ScreenLifecycle.Observer> implements Dumpable { public static final int SCREEN_OFF = 0; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java index d17f2f621ec8..5161deb0e4de 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.os.Trace; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -27,12 +28,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import javax.inject.Inject; -import javax.inject.Singleton; /** * Tracks the wakefulness lifecycle. */ -@Singleton +@SysUISingleton public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observer> implements Dumpable { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index 83c95b77b716..7c5dcd8960c4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -24,19 +24,19 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardViewController; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.statusbar.phone.NavigationModeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.DeviceConfigProxy; +import com.android.systemui.util.InjectionInflationController; import java.util.concurrent.Executor; -import javax.inject.Singleton; - import dagger.Lazy; import dagger.Module; import dagger.Provides; @@ -50,7 +50,7 @@ public class KeyguardModule { * Provides our instance of KeyguardViewMediator which is considered optional. */ @Provides - @Singleton + @SysUISingleton public static KeyguardViewMediator newKeyguardViewMediator( Context context, FalsingManager falsingManager, @@ -64,7 +64,8 @@ public class KeyguardModule { TrustManager trustManager, @UiBackground Executor uiBgExecutor, DeviceConfigProxy deviceConfig, - NavigationModeController navigationModeController) { + NavigationModeController navigationModeController, + InjectionInflationController injectionInflationController) { return new KeyguardViewMediator( context, falsingManager, @@ -78,6 +79,7 @@ public class KeyguardModule { powerManager, trustManager, deviceConfig, - navigationModeController); + navigationModeController, + injectionInflationController); } } diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index 16f76deca6c6..6db408659c96 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -20,6 +20,7 @@ import android.content.ContentResolver; import android.os.Build; import android.os.Looper; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.log.LogBuffer; @@ -27,8 +28,6 @@ import com.android.systemui.log.LogcatEchoTracker; import com.android.systemui.log.LogcatEchoTrackerDebug; import com.android.systemui.log.LogcatEchoTrackerProd; -import javax.inject.Singleton; - import dagger.Module; import dagger.Provides; @@ -39,7 +38,7 @@ import dagger.Provides; public class LogModule { /** Provides a logging buffer for doze-related logs. */ @Provides - @Singleton + @SysUISingleton @DozeLog public static LogBuffer provideDozeLogBuffer( LogcatEchoTracker bufferFilter, @@ -51,7 +50,7 @@ public class LogModule { /** Provides a logging buffer for all logs related to the data layer of notifications. */ @Provides - @Singleton + @SysUISingleton @NotificationLog public static LogBuffer provideNotificationsLogBuffer( LogcatEchoTracker bufferFilter, @@ -63,7 +62,7 @@ public class LogModule { /** Provides a logging buffer for all logs related to managing notification sections. */ @Provides - @Singleton + @SysUISingleton @NotificationSectionLog public static LogBuffer provideNotificationSectionLogBuffer( LogcatEchoTracker bufferFilter, @@ -75,7 +74,7 @@ public class LogModule { /** Provides a logging buffer for all logs related to the data layer of notifications. */ @Provides - @Singleton + @SysUISingleton @NotifInteractionLog public static LogBuffer provideNotifInteractionLogBuffer( LogcatEchoTracker echoTracker, @@ -87,7 +86,7 @@ public class LogModule { /** Provides a logging buffer for all logs related to Quick Settings. */ @Provides - @Singleton + @SysUISingleton @QSLog public static LogBuffer provideQuickSettingsLogBuffer( LogcatEchoTracker bufferFilter, @@ -99,7 +98,7 @@ public class LogModule { /** Provides a logging buffer for {@link com.android.systemui.broadcast.BroadcastDispatcher} */ @Provides - @Singleton + @SysUISingleton @BroadcastDispatcherLog public static LogBuffer provideBroadcastDispatcherLogBuffer( LogcatEchoTracker bufferFilter, @@ -111,7 +110,7 @@ public class LogModule { /** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */ @Provides - @Singleton + @SysUISingleton public static LogcatEchoTracker provideLogcatEchoTracker( ContentResolver contentResolver, @Main Looper looper) { diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt index f9c6307394e8..094ece27fc8e 100644 --- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt @@ -17,6 +17,7 @@ package com.android.systemui.media import android.view.View +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.StatusBarState @@ -24,13 +25,12 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.stack.MediaHeaderView import com.android.systemui.statusbar.phone.KeyguardBypassController import javax.inject.Inject -import javax.inject.Singleton /** * A class that controls the media notifications on the lock screen, handles its visibility and * is responsible for the embedding of he media experience. */ -@Singleton +@SysUISingleton class KeyguardMediaController @Inject constructor( private val mediaHost: MediaHost, private val bypassController: KeyguardBypassController, diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index 7f610d1a8467..a003d8365810 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -10,20 +10,22 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.LinearLayout +import androidx.annotation.VisibleForTesting import com.android.systemui.R +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager import com.android.systemui.qs.PageIndicator -import com.android.systemui.statusbar.notification.VisualStabilityManager +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.Utils import com.android.systemui.util.animation.UniqueObjectHostView import com.android.systemui.util.animation.requiresRemeasuring import com.android.systemui.util.concurrency.DelayableExecutor +import java.util.TreeMap import javax.inject.Inject import javax.inject.Provider -import javax.inject.Singleton private const val TAG = "MediaCarouselController" private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS) @@ -32,7 +34,7 @@ private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS) * Class that is responsible for keeping the view carousel up to date. * This also handles changes in state and applies them to the media carousel like the expansion. */ -@Singleton +@SysUISingleton class MediaCarouselController @Inject constructor( private val context: Context, private val mediaControlPanelFactory: Provider<MediaControlPanel>, @@ -40,7 +42,7 @@ class MediaCarouselController @Inject constructor( private val mediaHostStatesManager: MediaHostStatesManager, private val activityStarter: ActivityStarter, @Main executor: DelayableExecutor, - mediaManager: MediaDataFilter, + mediaManager: MediaDataManager, configurationController: ConfigurationController, falsingManager: FalsingManager ) { @@ -102,9 +104,7 @@ class MediaCarouselController @Inject constructor( private val mediaCarousel: MediaScrollView private val mediaCarouselScrollHandler: MediaCarouselScrollHandler val mediaFrame: ViewGroup - val mediaPlayers: MutableMap<String, MediaControlPanel> = mutableMapOf() private lateinit var settingsButton: View - private val mediaData: MutableMap<String, MediaData> = mutableMapOf() private val mediaContent: ViewGroup private val pageIndicator: PageIndicator private val visualStabilityCallback: VisualStabilityManager.Callback @@ -122,7 +122,7 @@ class MediaCarouselController @Inject constructor( set(value) { if (field != value) { field = value - for (player in mediaPlayers.values) { + for (player in MediaPlayerData.players()) { player.setListening(field) } } @@ -155,6 +155,7 @@ class MediaCarouselController @Inject constructor( inflateSettingsButton() mediaContent = mediaCarousel.requireViewById(R.id.media_carousel) configurationController.addCallback(configListener) + // TODO (b/162832756): remove visual stability manager when migrating to new pipeline visualStabilityCallback = VisualStabilityManager.Callback { if (needsReordering) { needsReordering = false @@ -167,20 +168,18 @@ class MediaCarouselController @Inject constructor( true /* persistent */) mediaManager.addListener(object : MediaDataManager.Listener { override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { - oldKey?.let { mediaData.remove(it) } if (!data.active && !Utils.useMediaResumption(context)) { // This view is inactive, let's remove this! This happens e.g when dismissing / // timing out a view. We still have the data around because resumption could // be on, but we should save the resources and release this. + oldKey?.let { MediaPlayerData.removeMediaPlayer(it) } onMediaDataRemoved(key) } else { - mediaData.put(key, data) addOrUpdatePlayer(key, oldKey, data) } } override fun onMediaDataRemoved(key: String) { - mediaData.remove(key) removePlayer(key) } }) @@ -223,53 +222,36 @@ class MediaCarouselController @Inject constructor( } private fun reorderAllPlayers() { - for (mediaPlayer in mediaPlayers.values) { - val view = mediaPlayer.view?.player - if (mediaPlayer.isPlaying && mediaContent.indexOfChild(view) != 0) { - mediaContent.removeView(view) - mediaContent.addView(view, 0) + mediaContent.removeAllViews() + for (mediaPlayer in MediaPlayerData.players()) { + mediaPlayer.view?.let { + mediaContent.addView(it.player) } } mediaCarouselScrollHandler.onPlayersChanged() } private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData) { - // If the key was changed, update entry - val oldData = mediaPlayers[oldKey] - if (oldData != null) { - val oldData = mediaPlayers.remove(oldKey) - mediaPlayers.put(key, oldData!!)?.let { - Log.wtf(TAG, "new key $key already exists when migrating from $oldKey") - } - } - var existingPlayer = mediaPlayers[key] + val existingPlayer = MediaPlayerData.getMediaPlayer(key, oldKey) if (existingPlayer == null) { - existingPlayer = mediaControlPanelFactory.get() - existingPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context), - mediaContent)) - existingPlayer.mediaViewController.sizeChangedListener = this::updateCarouselDimensions - mediaPlayers[key] = existingPlayer + var newPlayer = mediaControlPanelFactory.get() + newPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context), mediaContent)) + newPlayer.mediaViewController.sizeChangedListener = this::updateCarouselDimensions + MediaPlayerData.addMediaPlayer(key, data, newPlayer) val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) - existingPlayer.view?.player?.setLayoutParams(lp) - existingPlayer.bind(data) - existingPlayer.setListening(currentlyExpanded) - updatePlayerToState(existingPlayer, noAnimation = true) - if (existingPlayer.isPlaying) { - mediaContent.addView(existingPlayer.view?.player, 0) - } else { - mediaContent.addView(existingPlayer.view?.player) - } + newPlayer.view?.player?.setLayoutParams(lp) + newPlayer.bind(data) + newPlayer.setListening(currentlyExpanded) + updatePlayerToState(newPlayer, noAnimation = true) + reorderAllPlayers() } else { existingPlayer.bind(data) - if (existingPlayer.isPlaying && - mediaContent.indexOfChild(existingPlayer.view?.player) != 0) { - if (visualStabilityManager.isReorderingAllowed) { - mediaContent.removeView(existingPlayer.view?.player) - mediaContent.addView(existingPlayer.view?.player, 0) - } else { - needsReordering = true - } + MediaPlayerData.addMediaPlayer(key, data, existingPlayer) + if (visualStabilityManager.isReorderingAllowed) { + reorderAllPlayers() + } else { + needsReordering = true } } updatePageIndicator() @@ -277,13 +259,13 @@ class MediaCarouselController @Inject constructor( mediaCarousel.requiresRemeasuring = true // Check postcondition: mediaContent should have the same number of children as there are // elements in mediaPlayers. - if (mediaPlayers.size != mediaContent.childCount) { + if (MediaPlayerData.players().size != mediaContent.childCount) { Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync") } } private fun removePlayer(key: String) { - val removed = mediaPlayers.remove(key) + val removed = MediaPlayerData.removeMediaPlayer(key) removed?.apply { mediaCarouselScrollHandler.onPrePlayerRemoved(removed) mediaContent.removeView(removed.view?.player) @@ -294,12 +276,7 @@ class MediaCarouselController @Inject constructor( } private fun recreatePlayers() { - // Note that this will scramble the order of players. Actively playing sessions will, at - // least, still be put in the front. If we want to maintain order, then more work is - // needed. - mediaData.forEach { - key, data -> - removePlayer(key) + MediaPlayerData.mediaData().forEach { (key, data) -> addOrUpdatePlayer(key = key, oldKey = null, data = data) } } @@ -337,7 +314,7 @@ class MediaCarouselController @Inject constructor( currentStartLocation = startLocation currentEndLocation = endLocation currentTransitionProgress = progress - for (mediaPlayer in mediaPlayers.values) { + for (mediaPlayer in MediaPlayerData.players()) { updatePlayerToState(mediaPlayer, immediately) } maybeResetSettingsCog() @@ -386,7 +363,7 @@ class MediaCarouselController @Inject constructor( private fun updateCarouselDimensions() { var width = 0 var height = 0 - for (mediaPlayer in mediaPlayers.values) { + for (mediaPlayer in MediaPlayerData.players()) { val controller = mediaPlayer.mediaViewController // When transitioning the view to gone, the view gets smaller, but the translation // Doesn't, let's add the translation @@ -448,7 +425,7 @@ class MediaCarouselController @Inject constructor( this.desiredLocation = desiredLocation this.desiredHostState = it currentlyExpanded = it.expansion > 0 - for (mediaPlayer in mediaPlayers.values) { + for (mediaPlayer in MediaPlayerData.players()) { if (animate) { mediaPlayer.mediaViewController.animatePendingStateChange( duration = duration, @@ -470,7 +447,7 @@ class MediaCarouselController @Inject constructor( } fun closeGuts() { - mediaPlayers.values.forEach { + MediaPlayerData.players().forEach { it.closeGuts(true) } } @@ -497,3 +474,50 @@ class MediaCarouselController @Inject constructor( } } } + +@VisibleForTesting +internal object MediaPlayerData { + private data class MediaSortKey( + val data: MediaData, + val updateTime: Long = 0, + val isPlaying: Boolean = false + ) + + private val comparator = + compareByDescending<MediaSortKey> { it.isPlaying } + .thenByDescending { it.data.isLocalSession } + .thenByDescending { !it.data.resumption } + .thenByDescending { it.updateTime } + + private val mediaPlayers = TreeMap<MediaSortKey, MediaControlPanel>(comparator) + private val mediaData: MutableMap<String, MediaSortKey> = mutableMapOf() + + fun addMediaPlayer(key: String, data: MediaData, player: MediaControlPanel) { + removeMediaPlayer(key) + val sortKey = MediaSortKey(data, System.currentTimeMillis(), player.isPlaying()) + mediaData.put(key, sortKey) + mediaPlayers.put(sortKey, player) + } + + fun getMediaPlayer(key: String, oldKey: String?): MediaControlPanel? { + // If the key was changed, update entry + oldKey?.let { + if (it != key) { + mediaData.remove(it)?.let { sortKey -> mediaData.put(key, sortKey) } + } + } + return mediaData.get(key)?.let { mediaPlayers.get(it) } + } + + fun removeMediaPlayer(key: String) = mediaData.remove(key)?.let { mediaPlayers.remove(it) } + + fun mediaData() = mediaData.entries.map { e -> Pair(e.key, e.value.data) } + + fun players() = mediaPlayers.values + + @VisibleForTesting + fun clear() { + mediaData.clear() + mediaPlayers.clear() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt index dafc52ad8025..d6a02687c905 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt @@ -82,6 +82,10 @@ data class MediaData( */ var resumeAction: Runnable?, /** + * Local or remote playback + */ + var isLocalSession: Boolean = true, + /** * Indicates that this player is a resumption player (ie. It only shows a play actions which * will start the app and start playing). */ diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt index d0642ccf9714..aa3699e9a22b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt @@ -17,65 +17,48 @@ package com.android.systemui.media import javax.inject.Inject -import javax.inject.Singleton /** - * Combines updates from [MediaDataManager] with [MediaDeviceManager]. + * Combines [MediaDataManager.Listener] events with [MediaDeviceManager.Listener] events. */ -@Singleton -class MediaDataCombineLatest @Inject constructor( - private val dataSource: MediaDataManager, - private val deviceSource: MediaDeviceManager -) { +class MediaDataCombineLatest @Inject constructor() : MediaDataManager.Listener, + MediaDeviceManager.Listener { + private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf() private val entries: MutableMap<String, Pair<MediaData?, MediaDeviceData?>> = mutableMapOf() - init { - dataSource.addListener(object : MediaDataManager.Listener { - override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { - if (oldKey != null && oldKey != key && entries.contains(oldKey)) { - entries[key] = data to entries.remove(oldKey)?.second - update(key, oldKey) - } else { - entries[key] = data to entries[key]?.second - update(key, key) - } - } - override fun onMediaDataRemoved(key: String) { - remove(key) - } - }) - deviceSource.addListener(object : MediaDeviceManager.Listener { - override fun onMediaDeviceChanged( - key: String, - oldKey: String?, - data: MediaDeviceData? - ) { - if (oldKey != null && oldKey != key && entries.contains(oldKey)) { - entries[key] = entries.remove(oldKey)?.first to data - update(key, oldKey) - } else { - entries[key] = entries[key]?.first to data - update(key, key) - } - } - override fun onKeyRemoved(key: String) { - remove(key) - } - }) + override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { + if (oldKey != null && oldKey != key && entries.contains(oldKey)) { + entries[key] = data to entries.remove(oldKey)?.second + update(key, oldKey) + } else { + entries[key] = data to entries[key]?.second + update(key, key) + } } - /** - * Get a map of all non-null data entries - */ - fun getData(): Map<String, MediaData> { - return entries.filter { - (key, pair) -> pair.first != null && pair.second != null - }.mapValues { - (key, pair) -> pair.first!!.copy(device = pair.second) + override fun onMediaDataRemoved(key: String) { + remove(key) + } + + override fun onMediaDeviceChanged( + key: String, + oldKey: String?, + data: MediaDeviceData? + ) { + if (oldKey != null && oldKey != key && entries.contains(oldKey)) { + entries[key] = entries.remove(oldKey)?.first to data + update(key, oldKey) + } else { + entries[key] = entries[key]?.first to data + update(key, key) } } + override fun onKeyRemoved(key: String) { + remove(key) + } + /** * Add a listener for [MediaData] changes that has been combined with latest [MediaDeviceData]. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt index 24ca9708a4e3..0664a41f841d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt @@ -24,7 +24,6 @@ import com.android.systemui.settings.CurrentUserTracker import com.android.systemui.statusbar.NotificationLockscreenUserManager import java.util.concurrent.Executor import javax.inject.Inject -import javax.inject.Singleton private const val TAG = "MediaDataFilter" private const val DEBUG = true @@ -33,24 +32,24 @@ private const val DEBUG = true * Filters data updates from [MediaDataCombineLatest] based on the current user ID, and handles user * switches (removing entries for the previous user, adding back entries for the current user) * - * This is added downstream of [MediaDataManager] since we may still need to handle callbacks from - * background users (e.g. timeouts) that UI classes should ignore. - * Instead, UI classes should listen to this so they can stay in sync with the current user. + * This is added at the end of the pipeline since we may still need to handle callbacks from + * background users (e.g. timeouts). */ -@Singleton class MediaDataFilter @Inject constructor( - private val dataSource: MediaDataCombineLatest, private val broadcastDispatcher: BroadcastDispatcher, private val mediaResumeListener: MediaResumeListener, - private val mediaDataManager: MediaDataManager, private val lockscreenUserManager: NotificationLockscreenUserManager, @Main private val executor: Executor ) : MediaDataManager.Listener { private val userTracker: CurrentUserTracker - private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf() + private val _listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf() + internal val listeners: Set<MediaDataManager.Listener> + get() = _listeners.toSet() + internal lateinit var mediaDataManager: MediaDataManager - // The filtered mediaEntries, which will be a subset of all mediaEntries in MediaDataManager - private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() + private val allEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() + // The filtered userEntries, which will be a subset of all userEntries in MediaDataManager + private val userEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() init { userTracker = object : CurrentUserTracker(broadcastDispatcher) { @@ -60,31 +59,34 @@ class MediaDataFilter @Inject constructor( } } userTracker.startTracking() - dataSource.addListener(this) } override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { + if (oldKey != null && oldKey != key) { + allEntries.remove(oldKey) + } + allEntries.put(key, data) + if (!lockscreenUserManager.isCurrentProfile(data.userId)) { return } - if (oldKey != null) { - mediaEntries.remove(oldKey) + if (oldKey != null && oldKey != key) { + userEntries.remove(oldKey) } - mediaEntries.put(key, data) + userEntries.put(key, data) // Notify listeners - val listenersCopy = listeners.toSet() - listenersCopy.forEach { + listeners.forEach { it.onMediaDataLoaded(key, oldKey, data) } } override fun onMediaDataRemoved(key: String) { - mediaEntries.remove(key)?.let { + allEntries.remove(key) + userEntries.remove(key)?.let { // Only notify listeners if something actually changed - val listenersCopy = listeners.toSet() - listenersCopy.forEach { + listeners.forEach { it.onMediaDataRemoved(key) } } @@ -93,11 +95,11 @@ class MediaDataFilter @Inject constructor( @VisibleForTesting internal fun handleUserSwitched(id: Int) { // If the user changes, remove all current MediaData objects and inform listeners - val listenersCopy = listeners.toSet() - val keyCopy = mediaEntries.keys.toMutableList() + val listenersCopy = listeners + val keyCopy = userEntries.keys.toMutableList() // Clear the list first, to make sure callbacks from listeners if we have any entries // are up to date - mediaEntries.clear() + userEntries.clear() keyCopy.forEach { if (DEBUG) Log.d(TAG, "Removing $it after user change") listenersCopy.forEach { listener -> @@ -105,10 +107,10 @@ class MediaDataFilter @Inject constructor( } } - dataSource.getData().forEach { (key, data) -> + allEntries.forEach { (key, data) -> if (lockscreenUserManager.isCurrentProfile(data.userId)) { if (DEBUG) Log.d(TAG, "Re-adding $key after user change") - mediaEntries.put(key, data) + userEntries.put(key, data) listenersCopy.forEach { listener -> listener.onMediaDataLoaded(key, null, data) } @@ -121,7 +123,7 @@ class MediaDataFilter @Inject constructor( */ fun onSwipeToDismiss() { if (DEBUG) Log.d(TAG, "Media carousel swiped away") - val mediaKeys = mediaEntries.keys.toSet() + val mediaKeys = userEntries.keys.toSet() mediaKeys.forEach { mediaDataManager.setTimedOut(it, timedOut = true) } @@ -130,7 +132,7 @@ class MediaDataFilter @Inject constructor( /** * Are there any media notifications active? */ - fun hasActiveMedia() = mediaEntries.any { it.value.active } + fun hasActiveMedia() = userEntries.any { it.value.active } /** * Are there any media entries we should display? @@ -138,7 +140,7 @@ class MediaDataFilter @Inject constructor( * If resumption is disabled, we only want to show active players */ fun hasAnyMedia() = if (mediaResumeListener.isResumptionEnabled()) { - mediaEntries.isNotEmpty() + userEntries.isNotEmpty() } else { hasActiveMedia() } @@ -146,10 +148,10 @@ class MediaDataFilter @Inject constructor( /** * Add a listener for filtered [MediaData] changes */ - fun addListener(listener: MediaDataManager.Listener) = listeners.add(listener) + fun addListener(listener: MediaDataManager.Listener) = _listeners.add(listener) /** * Remove a listener that was registered with addListener */ - fun removeListener(listener: MediaDataManager.Listener) = listeners.remove(listener) -}
\ No newline at end of file + fun removeListener(listener: MediaDataManager.Listener) = _listeners.remove(listener) +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 8a51c8515852..686531acb6f9 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -31,6 +31,7 @@ import android.graphics.drawable.Drawable import android.graphics.drawable.Icon import android.media.MediaDescription import android.media.MediaMetadata +import android.media.session.MediaController import android.media.session.MediaSession import android.net.Uri import android.os.UserHandle @@ -41,9 +42,11 @@ import com.android.internal.graphics.ColorUtils import com.android.systemui.Dumpable import com.android.systemui.R import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager +import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.notification.MediaNotificationProcessor import com.android.systemui.statusbar.notification.row.HybridGroupManager import com.android.systemui.util.Assert @@ -54,7 +57,6 @@ import java.io.IOException import java.io.PrintWriter import java.util.concurrent.Executor import javax.inject.Inject -import javax.inject.Singleton // URI fields to try loading album art from private val ART_URIS = arrayOf( @@ -87,7 +89,7 @@ fun isMediaNotification(sbn: StatusBarNotification): Boolean { /** * A class that facilitates management and loading of Media Data, ready for binding. */ -@Singleton +@SysUISingleton class MediaDataManager( private val context: Context, @Background private val backgroundExecutor: Executor, @@ -97,12 +99,35 @@ class MediaDataManager( dumpManager: DumpManager, mediaTimeoutListener: MediaTimeoutListener, mediaResumeListener: MediaResumeListener, + mediaSessionBasedFilter: MediaSessionBasedFilter, + mediaDeviceManager: MediaDeviceManager, + mediaDataCombineLatest: MediaDataCombineLatest, + private val mediaDataFilter: MediaDataFilter, + private val activityStarter: ActivityStarter, private var useMediaResumption: Boolean, private val useQsMediaPlayer: Boolean ) : Dumpable { - private val listeners: MutableSet<Listener> = mutableSetOf() + // Internal listeners are part of the internal pipeline. External listeners (those registered + // with [MediaDeviceManager.addListener]) receive events after they have propagated through + // the internal pipeline. + // Another way to think of the distinction between internal and external listeners is the + // following. Internal listeners are listeners that MediaDataManager depends on, and external + // listeners are listeners that depend on MediaDataManager. + // TODO(b/159539991#comment5): Move internal listeners to separate package. + private val internalListeners: MutableSet<Listener> = mutableSetOf() private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() + internal var appsBlockedFromResume: MutableSet<String> = Utils.getBlockedMediaApps(context) + set(value) { + // Update list + appsBlockedFromResume.clear() + appsBlockedFromResume.addAll(value) + + // Remove any existing resume players that are now blocked + appsBlockedFromResume.forEach { + removeAllForPackage(it) + } + } @Inject constructor( @@ -113,10 +138,16 @@ class MediaDataManager( dumpManager: DumpManager, broadcastDispatcher: BroadcastDispatcher, mediaTimeoutListener: MediaTimeoutListener, - mediaResumeListener: MediaResumeListener + mediaResumeListener: MediaResumeListener, + mediaSessionBasedFilter: MediaSessionBasedFilter, + mediaDeviceManager: MediaDeviceManager, + mediaDataCombineLatest: MediaDataCombineLatest, + mediaDataFilter: MediaDataFilter, + activityStarter: ActivityStarter ) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory, broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener, - Utils.useMediaResumption(context), Utils.useQsMediaPlayer(context)) + mediaSessionBasedFilter, mediaDeviceManager, mediaDataCombineLatest, mediaDataFilter, + activityStarter, Utils.useMediaResumption(context), Utils.useQsMediaPlayer(context)) private val appChangeReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { @@ -138,12 +169,26 @@ class MediaDataManager( init { dumpManager.registerDumpable(TAG, this) + + // Initialize the internal processing pipeline. The listeners at the front of the pipeline + // are set as internal listeners so that they receive events. From there, events are + // propagated through the pipeline. The end of the pipeline is currently mediaDataFilter, + // so it is responsible for dispatching events to external listeners. To achieve this, + // external listeners that are registered with [MediaDataManager.addListener] are actually + // registered as listeners to mediaDataFilter. + addInternalListener(mediaTimeoutListener) + addInternalListener(mediaResumeListener) + addInternalListener(mediaSessionBasedFilter) + mediaSessionBasedFilter.addListener(mediaDeviceManager) + mediaSessionBasedFilter.addListener(mediaDataCombineLatest) + mediaDeviceManager.addListener(mediaDataCombineLatest) + mediaDataCombineLatest.addListener(mediaDataFilter) + + // Set up links back into the pipeline for listeners that need to send events upstream. mediaTimeoutListener.timeoutCallback = { token: String, timedOut: Boolean -> setTimedOut(token, timedOut) } - addListener(mediaTimeoutListener) - mediaResumeListener.setManager(this) - addListener(mediaResumeListener) + mediaDataFilter.mediaDataManager = this val suspendFilter = IntentFilter(Intent.ACTION_PACKAGES_SUSPENDED) broadcastDispatcher.registerReceiver(appChangeReceiver, suspendFilter, null, UserHandle.ALL) @@ -181,10 +226,9 @@ class MediaDataManager( private fun removeAllForPackage(packageName: String) { Assert.isMainThread() - val listenersCopy = listeners.toSet() val toRemove = mediaEntries.filter { it.value.packageName == packageName } toRemove.forEach { - removeEntry(it.key, listenersCopy) + removeEntry(it.key) } } @@ -244,12 +288,45 @@ class MediaDataManager( /** * Add a listener for changes in this class */ - fun addListener(listener: Listener) = listeners.add(listener) + fun addListener(listener: Listener) { + // mediaDataFilter is the current end of the internal pipeline. Register external + // listeners as listeners to it. + mediaDataFilter.addListener(listener) + } /** * Remove a listener for changes in this class */ - fun removeListener(listener: Listener) = listeners.remove(listener) + fun removeListener(listener: Listener) { + // Since mediaDataFilter is the current end of the internal pipelie, external listeners + // have been registered to it. So, they need to be removed from it too. + mediaDataFilter.removeListener(listener) + } + + /** + * Add a listener for internal events. + */ + private fun addInternalListener(listener: Listener) = internalListeners.add(listener) + + /** + * Notify internal listeners of loaded event. + * + * External listeners registered with [addListener] will be notified after the event propagates + * through the internal listener pipeline. + */ + private fun notifyMediaDataLoaded(key: String, oldKey: String?, info: MediaData) { + internalListeners.forEach { it.onMediaDataLoaded(key, oldKey, info) } + } + + /** + * Notify internal listeners of removed event. + * + * External listeners registered with [addListener] will be notified after the event propagates + * through the internal listener pipeline. + */ + private fun notifyMediaDataRemoved(key: String) { + internalListeners.forEach { it.onMediaDataRemoved(key) } + } /** * Called whenever the player has been paused or stopped for a while, or swiped from QQS. @@ -267,16 +344,13 @@ class MediaDataManager( } } - private fun removeEntry(key: String, listenersCopy: Set<Listener>) { + private fun removeEntry(key: String) { mediaEntries.remove(key) - listenersCopy.forEach { - it.onMediaDataRemoved(key) - } + notifyMediaDataRemoved(key) } fun dismissMediaData(key: String, delay: Long) { - val listenersCopy = listeners.toSet() - foregroundExecutor.executeDelayed({ removeEntry(key, listenersCopy) }, delay) + foregroundExecutor.executeDelayed({ removeEntry(key) }, delay) } private fun loadMediaDataInBgForResumption( @@ -328,7 +402,8 @@ class MediaDataManager( ) { val token = sbn.notification.extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) as MediaSession.Token? - val metadata = mediaControllerFactory.create(token).metadata + val mediaController = mediaControllerFactory.create(token) + val metadata = mediaController.metadata // Foreground and Background colors computed from album art val notif: Notification = sbn.notification @@ -403,10 +478,13 @@ class MediaDataManager( } val runnable = if (action.actionIntent != null) { Runnable { - try { - action.actionIntent.send() - } catch (e: PendingIntent.CanceledException) { - Log.d(TAG, "Intent canceled", e) + if (action.isAuthenticationRequired()) { + activityStarter.dismissKeyguardThenExecute({ + var result = sendPendingIntent(action.actionIntent) + result + }, {}, true) + } else { + sendPendingIntent(action.actionIntent) } } } else { @@ -420,6 +498,9 @@ class MediaDataManager( } } + val isLocalSession = mediaController.playbackInfo?.playbackType == + MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL ?: true + foregroundExecutor.execute { val resumeAction: Runnable? = mediaEntries[key]?.resumeAction val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true @@ -427,8 +508,8 @@ class MediaDataManager( onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, bgColor, app, smallIconDrawable, artist, song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null, - active, resumeAction = resumeAction, notificationKey = key, - hasCheckedForResume = hasCheckedForResume)) + active, resumeAction = resumeAction, isLocalSession = isLocalSession, + notificationKey = key, hasCheckedForResume = hasCheckedForResume)) } } @@ -449,6 +530,15 @@ class MediaDataManager( return null } + private fun sendPendingIntent(intent: PendingIntent): Boolean { + return try { + intent.send() + true + } catch (e: PendingIntent.CanceledException) { + Log.d(TAG, "Intent canceled", e) + false + } + } /** * Load a bitmap from a URI * @param uri the uri to load @@ -517,18 +607,16 @@ class MediaDataManager( if (mediaEntries.containsKey(key)) { // Otherwise this was removed already mediaEntries.put(key, data) - val listenersCopy = listeners.toSet() - listenersCopy.forEach { - it.onMediaDataLoaded(key, oldKey, data) - } + notifyMediaDataLoaded(key, oldKey, data) } } fun onNotificationRemoved(key: String) { Assert.isMainThread() val removed = mediaEntries.remove(key) - if (useMediaResumption && removed?.resumeAction != null) { - if (DEBUG) Log.d(TAG, "Not removing $key because resumable") + if (useMediaResumption && removed?.resumeAction != null && + !isBlockedFromResume(removed?.packageName)) { + Log.d(TAG, "Not removing $key because resumable") // Move to resume key (aka package name) if that key doesn't already exist. val resumeAction = getResumeMediaAction(removed.resumeAction!!) val updated = removed.copy(token = null, actions = listOf(resumeAction), @@ -536,32 +624,29 @@ class MediaDataManager( val pkg = removed?.packageName val migrate = mediaEntries.put(pkg, updated) == null // Notify listeners of "new" controls when migrating or removed and update when not - val listenersCopy = listeners.toSet() if (migrate) { - listenersCopy.forEach { - it.onMediaDataLoaded(pkg, key, updated) - } + notifyMediaDataLoaded(pkg, key, updated) } else { // Since packageName is used for the key of the resumption controls, it is // possible that another notification has already been reused for the resumption // controls of this package. In this case, rather than renaming this player as // packageName, just remove it and then send a update to the existing resumption // controls. - listenersCopy.forEach { - it.onMediaDataRemoved(key) - } - listenersCopy.forEach { - it.onMediaDataLoaded(pkg, pkg, updated) - } + notifyMediaDataRemoved(key) + notifyMediaDataLoaded(pkg, pkg, updated) } return } if (removed != null) { - val listenersCopy = listeners.toSet() - listenersCopy.forEach { - it.onMediaDataRemoved(key) - } + notifyMediaDataRemoved(key) + } + } + + private fun isBlockedFromResume(packageName: String?): Boolean { + if (packageName == null) { + return true } + return appsBlockedFromResume.contains(packageName) } fun setMediaResumptionEnabled(isEnabled: Boolean) { @@ -573,17 +658,31 @@ class MediaDataManager( if (!useMediaResumption) { // Remove any existing resume controls - val listenersCopy = listeners.toSet() val filtered = mediaEntries.filter { !it.value.active } filtered.forEach { mediaEntries.remove(it.key) - listenersCopy.forEach { listener -> - listener.onMediaDataRemoved(it.key) - } + notifyMediaDataRemoved(it.key) } } } + /** + * Invoked when the user has dismissed the media carousel + */ + fun onSwipeToDismiss() = mediaDataFilter.onSwipeToDismiss() + + /** + * Are there any media notifications active? + */ + fun hasActiveMedia() = mediaDataFilter.hasActiveMedia() + + /** + * Are there any media entries we should display? + * If resumption is enabled, this will include inactive players + * If resumption is disabled, we only want to show active players + */ + fun hasAnyMedia() = mediaDataFilter.hasAnyMedia() + interface Listener { /** @@ -603,9 +702,11 @@ class MediaDataManager( override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { pw.apply { - println("listeners: $listeners") + println("internalListeners: $internalListeners") + println("externalListeners: ${mediaDataFilter.listeners}") println("mediaEntries: $mediaEntries") println("useMediaResumption: $useMediaResumption") + println("appsBlockedFromResume: $appsBlockedFromResume") } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt index ae7f66b5ac48..2bc908be055c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt @@ -24,34 +24,32 @@ import androidx.annotation.MainThread import androidx.annotation.WorkerThread import com.android.settingslib.media.LocalMediaManager import com.android.settingslib.media.MediaDevice +import com.android.systemui.Dumpable +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.Dumpable import com.android.systemui.dump.DumpManager import java.io.FileDescriptor import java.io.PrintWriter import java.util.concurrent.Executor import javax.inject.Inject -import javax.inject.Singleton /** * Provides information about the route (ie. device) where playback is occurring. */ -@Singleton class MediaDeviceManager @Inject constructor( private val context: Context, private val localMediaManagerFactory: LocalMediaManagerFactory, private val mr2manager: MediaRouter2Manager, @Main private val fgExecutor: Executor, @Background private val bgExecutor: Executor, - private val mediaDataManager: MediaDataManager, - private val dumpManager: DumpManager + dumpManager: DumpManager ) : MediaDataManager.Listener, Dumpable { + private val listeners: MutableSet<Listener> = mutableSetOf() private val entries: MutableMap<String, Entry> = mutableMapOf() init { - mediaDataManager.addListener(this) dumpManager.registerDumpable(javaClass.name, this) } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index 70f01d576a9c..5475a00ced3c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -27,6 +27,7 @@ import android.view.View import android.view.ViewGroup import android.view.ViewGroupOverlay import com.android.systemui.Interpolators +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.NotificationLockscreenUserManager @@ -37,7 +38,6 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.animation.UniqueObjectHostView import javax.inject.Inject -import javax.inject.Singleton /** * Similarly to isShown but also excludes views that have 0 alpha @@ -65,7 +65,7 @@ val View.isShownNotFaded: Boolean * This manager is responsible for placement of the unique media view between the different hosts * and animate the positions of the views to achieve seamless transitions. */ -@Singleton +@SysUISingleton class MediaHierarchyManager @Inject constructor( private val context: Context, private val statusBarStateController: SysuiStatusBarStateController, @@ -389,6 +389,14 @@ class MediaHierarchyManager @Inject constructor( if (isCurrentlyInGuidedTransformation()) { return false } + // This is an invalid transition, and can happen when using the camera gesture from the + // lock screen. Disallow. + if (previousLocation == LOCATION_LOCKSCREEN && + desiredLocation == LOCATION_QQS && + statusbarState == StatusBarState.SHADE) { + return false + } + if (currentLocation == LOCATION_QQS && previousLocation == LOCATION_LOCKSCREEN && (statusBarStateController.leaveOpenOnKeyguardHide() || @@ -604,8 +612,8 @@ class MediaHierarchyManager @Inject constructor( // When collapsing on the lockscreen, we want to remain in QS return LOCATION_QS } - if (location != LOCATION_LOCKSCREEN && desiredLocation == LOCATION_LOCKSCREEN - && !fullyAwake) { + if (location != LOCATION_LOCKSCREEN && desiredLocation == LOCATION_LOCKSCREEN && + !fullyAwake) { // When unlocking from dozing / while waking up, the media shouldn't be transitioning // in an animated way. Let's keep it in the lockscreen until we're fully awake and // reattach it without an animation diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt index 3598719fcb3a..ce184aa23a57 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt @@ -14,7 +14,7 @@ import javax.inject.Inject class MediaHost @Inject constructor( private val state: MediaHostStateHolder, private val mediaHierarchyManager: MediaHierarchyManager, - private val mediaDataFilter: MediaDataFilter, + private val mediaDataManager: MediaDataManager, private val mediaHostStatesManager: MediaHostStatesManager ) : MediaHostState by state { lateinit var hostView: UniqueObjectHostView @@ -79,12 +79,12 @@ class MediaHost @Inject constructor( // be a delay until the views and the controllers are initialized, leaving us // with either a blank view or the controllers not yet initialized and the // measuring wrong - mediaDataFilter.addListener(listener) + mediaDataManager.addListener(listener) updateViewVisibility() } override fun onViewDetachedFromWindow(v: View?) { - mediaDataFilter.removeListener(listener) + mediaDataManager.removeListener(listener) } }) @@ -113,9 +113,9 @@ class MediaHost @Inject constructor( private fun updateViewVisibility() { visible = if (showsOnlyActiveMedia) { - mediaDataFilter.hasActiveMedia() + mediaDataManager.hasActiveMedia() } else { - mediaDataFilter.hasAnyMedia() + mediaDataManager.hasAnyMedia() } val newVisibility = if (visible) View.VISIBLE else View.GONE if (newVisibility != hostView.visibility) { @@ -289,4 +289,4 @@ interface MediaHostState { * Get a copy of this view state, deepcopying all appropriate members */ fun copy(): MediaHostState -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt index d3954b70ca71..ba7c1679b174 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt @@ -16,16 +16,16 @@ package com.android.systemui.media +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.util.animation.MeasurementOutput import javax.inject.Inject -import javax.inject.Singleton /** * A class responsible for managing all media host states of the various host locations and * coordinating the heights among different players. This class can be used to get the most up to * date state for any location. */ -@Singleton +@SysUISingleton class MediaHostStatesManager @Inject constructor() { private val callbacks: MutableSet<Callback> = mutableSetOf() diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt index 4ec746fcb153..c00b5e92f93d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt @@ -29,20 +29,20 @@ import android.provider.Settings import android.service.media.MediaBrowserService import android.util.Log import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.tuner.TunerService import com.android.systemui.util.Utils import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.Executor import javax.inject.Inject -import javax.inject.Singleton private const val TAG = "MediaResumeListener" private const val MEDIA_PREFERENCES = "media_control_prefs" private const val MEDIA_PREFERENCE_KEY = "browser_components_" -@Singleton +@SysUISingleton class MediaResumeListener @Inject constructor( private val context: Context, private val broadcastDispatcher: BroadcastDispatcher, @@ -52,6 +52,7 @@ class MediaResumeListener @Inject constructor( private var useMediaResumption: Boolean = Utils.useMediaResumption(context) private val resumeComponents: ConcurrentLinkedQueue<ComponentName> = ConcurrentLinkedQueue() + private var blockedApps: MutableSet<String> = Utils.getBlockedMediaApps(context) private lateinit var mediaDataManager: MediaDataManager @@ -114,6 +115,14 @@ class MediaResumeListener @Inject constructor( mediaDataManager.setMediaResumptionEnabled(useMediaResumption) } }, Settings.Secure.MEDIA_CONTROLS_RESUME) + + // Listen to changes in which apps are allowed to persist + tunerService.addTunable(object : TunerService.Tunable { + override fun onTuningChanged(key: String?, newValue: String?) { + blockedApps = Utils.getBlockedMediaApps(context) + mediaDataManager.appsBlockedFromResume = blockedApps + } + }, Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED) } fun isResumptionEnabled() = useMediaResumption @@ -144,8 +153,10 @@ class MediaResumeListener @Inject constructor( } resumeComponents.forEach { - val browser = ResumeMediaBrowser(context, mediaBrowserCallback, it) - browser.findRecentMedia() + if (!blockedApps.contains(it.packageName)) { + val browser = ResumeMediaBrowser(context, mediaBrowserCallback, it) + browser.findRecentMedia() + } } } @@ -154,7 +165,8 @@ class MediaResumeListener @Inject constructor( // If this had been started from a resume state, disconnect now that it's live mediaBrowser?.disconnect() // If we don't have a resume action, check if we haven't already - if (data.resumeAction == null && !data.hasCheckedForResume) { + if (data.resumeAction == null && !data.hasCheckedForResume && + !blockedApps.contains(data.packageName)) { // TODO also check for a media button receiver intended for restarting (b/154127084) Log.d(TAG, "Checking for service component for " + data.packageName) val pm = context.packageManager diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt new file mode 100644 index 000000000000..f01713fb5f6c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.media + +import android.content.ComponentName +import android.content.Context +import android.media.session.MediaController +import android.media.session.MediaController.PlaybackInfo +import android.media.session.MediaSession +import android.media.session.MediaSessionManager +import android.util.Log +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins +import java.util.concurrent.Executor +import javax.inject.Inject + +private const val TAG = "MediaSessionBasedFilter" + +/** + * Filters media loaded events for local media sessions while an app is casting. + * + * When an app is casting there can be one remote media sessions and potentially more local media + * sessions. In this situation, there should only be a media object for the remote session. To + * achieve this, update events for the local session need to be filtered. + */ +class MediaSessionBasedFilter @Inject constructor( + context: Context, + private val sessionManager: MediaSessionManager, + @Main private val foregroundExecutor: Executor, + @Background private val backgroundExecutor: Executor +) : MediaDataManager.Listener { + + private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf() + + // Keep track of MediaControllers for a given package to check if an app is casting and it + // filter loaded events for local sessions. + private val packageControllers: LinkedHashMap<String, MutableList<MediaController>> = + LinkedHashMap() + + // Keep track of the key used for the session tokens. This information is used to know when + // dispatch a removed event so that a media object for a local session will be removed. + private val keyedTokens: MutableMap<String, MutableList<MediaSession.Token>> = mutableMapOf() + + private val sessionListener = object : MediaSessionManager.OnActiveSessionsChangedListener { + override fun onActiveSessionsChanged(controllers: List<MediaController>) { + handleControllersChanged(controllers) + } + } + + init { + backgroundExecutor.execute { + val name = ComponentName(context, NotificationListenerWithPlugins::class.java) + sessionManager.addOnActiveSessionsChangedListener(sessionListener, name) + handleControllersChanged(sessionManager.getActiveSessions(name)) + } + } + + /** + * Add a listener for filtered [MediaData] changes + */ + fun addListener(listener: MediaDataManager.Listener) = listeners.add(listener) + + /** + * Remove a listener that was registered with addListener + */ + fun removeListener(listener: MediaDataManager.Listener) = listeners.remove(listener) + + /** + * May filter loaded events by not passing them along to listeners. + * + * If an app has only one session with playback type PLAYBACK_TYPE_REMOTE, then assuming that + * the app is casting. Sometimes apps will send redundant updates to a local session with + * playback type PLAYBACK_TYPE_LOCAL. These updates should be filtered to improve the usability + * of the media controls. + */ + override fun onMediaDataLoaded(key: String, oldKey: String?, info: MediaData) { + backgroundExecutor.execute { + val isMigration = oldKey != null && key != oldKey + if (isMigration) { + keyedTokens.remove(oldKey)?.let { removed -> keyedTokens.put(key, removed) } + } + if (info.token != null) { + keyedTokens.get(key)?.let { + tokens -> + tokens.add(info.token) + } ?: run { + val tokens = mutableListOf(info.token) + keyedTokens.put(key, tokens) + } + } + // Determine if an app is casting by checking if it has a session with playback type + // PLAYBACK_TYPE_REMOTE. + val remoteControllers = packageControllers.get(info.packageName)?.filter { + it.playbackInfo?.playbackType == PlaybackInfo.PLAYBACK_TYPE_REMOTE + } + // Limiting search to only apps with a single remote session. + val remote = if (remoteControllers?.size == 1) remoteControllers.firstOrNull() else null + if (isMigration || remote == null || remote.sessionToken == info.token) { + // Not filtering in this case. Passing the event along to listeners. + dispatchMediaDataLoaded(key, oldKey, info) + } else { + // Filtering this event because the app is casting and the loaded events is for a + // local session. + Log.d(TAG, "filtering key=$key local=${info.token} remote=${remote?.sessionToken}") + // If the local session uses a different notification key, then lets go a step + // farther and dismiss the media data so that media controls for the local session + // don't hang around while casting. + if (!keyedTokens.get(key)!!.contains(remote.sessionToken)) { + dispatchMediaDataRemoved(key) + } + } + } + } + + override fun onMediaDataRemoved(key: String) { + // Queue on background thread to ensure ordering of loaded and removed events is maintained. + backgroundExecutor.execute { + keyedTokens.remove(key) + dispatchMediaDataRemoved(key) + } + } + + private fun dispatchMediaDataLoaded(key: String, oldKey: String?, info: MediaData) { + foregroundExecutor.execute { + listeners.toSet().forEach { it.onMediaDataLoaded(key, oldKey, info) } + } + } + + private fun dispatchMediaDataRemoved(key: String) { + foregroundExecutor.execute { + listeners.toSet().forEach { it.onMediaDataRemoved(key) } + } + } + + private fun handleControllersChanged(controllers: List<MediaController>) { + packageControllers.clear() + controllers.forEach { + controller -> + packageControllers.get(controller.packageName)?.let { + tokens -> + tokens.add(controller) + } ?: run { + val tokens = mutableListOf(controller) + packageControllers.put(controller.packageName, tokens) + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt index 8662aacfdab2..fdbff98ea831 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt @@ -20,12 +20,12 @@ import android.media.session.MediaController import android.media.session.PlaybackState import android.os.SystemProperties import android.util.Log +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState import com.android.systemui.util.concurrency.DelayableExecutor import java.util.concurrent.TimeUnit import javax.inject.Inject -import javax.inject.Singleton private const val DEBUG = true private const val TAG = "MediaTimeout" @@ -35,7 +35,7 @@ private val PAUSED_MEDIA_TIMEOUT = SystemProperties /** * Controller responsible for keeping track of playback states and expiring inactive streams. */ -@Singleton +@SysUISingleton class MediaTimeoutListener @Inject constructor( private val mediaControllerFactory: MediaControllerFactory, @Main private val mainExecutor: DelayableExecutor diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java index ccf58ba5daa8..5dce09386c7d 100644 --- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java +++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.util.Log; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.shared.system.QuickStepContract; import java.io.FileDescriptor; @@ -29,13 +30,11 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; -import javax.inject.Singleton; - /** * Contains sysUi state flags and notifies registered * listeners whenever changes happen. */ -@Singleton +@SysUISingleton public class SysUiState implements Dumpable { private static final String TAG = SysUiState.class.getSimpleName(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index fd653b4e8aa5..6c8a23ba96d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -1,18 +1,20 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2020 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 + * 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. + * 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 com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar; import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; @@ -21,6 +23,7 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.StatusBarManager.WindowType; import static android.app.StatusBarManager.WindowVisibleState; import static android.app.StatusBarManager.windowStateToString; +import static android.view.Display.DEFAULT_DISPLAY; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.containsType; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; @@ -78,17 +81,16 @@ import android.text.TextUtils; import android.util.Log; import android.view.Display; import android.view.Gravity; +import android.view.IWindowManager; import android.view.InsetsState.InternalInsetsType; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.Surface; import android.view.View; -import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.WindowInsetsController.Appearance; import android.view.WindowManager; -import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; @@ -102,6 +104,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.LatencyTracker; import com.android.internal.view.AppearanceRegion; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.accessibility.SystemActions; import com.android.systemui.assist.AssistHandleViewController; @@ -109,8 +112,12 @@ import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.fragments.FragmentHostManager; -import com.android.systemui.fragments.FragmentHostManager.FragmentListener; import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.buttons.ButtonDispatcher; +import com.android.systemui.navigationbar.buttons.KeyButtonView; +import com.android.systemui.navigationbar.buttons.RotationContextButton; +import com.android.systemui.navigationbar.gestural.QuickswitchOrientedNavHandle; +import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; @@ -123,29 +130,28 @@ import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; -import com.android.systemui.statusbar.phone.ContextualButton.ContextButtonListener; +import com.android.systemui.statusbar.phone.AutoHideController; +import com.android.systemui.statusbar.phone.BarTransitions; +import com.android.systemui.statusbar.phone.LightBarController; +import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.KeyButtonView; -import com.android.systemui.util.LifecycleFragment; -import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.List; import java.util.Locale; import java.util.Optional; import java.util.function.Consumer; -import javax.inject.Inject; - import dagger.Lazy; /** - * Fragment containing the NavigationBarFragment. Contains logic for what happens - * on clicks and view states of the nav bar. + * Contains logic for a navigation bar view. */ -public class NavigationBarFragment extends LifecycleFragment implements Callbacks, - NavigationModeController.ModeChangedListener, DisplayManager.DisplayListener { +public class NavigationBar implements View.OnAttachStateChangeListener, + Callbacks, NavigationModeController.ModeChangedListener, DisplayManager.DisplayListener { public static final String TAG = "NavigationBar"; private static final boolean DEBUG = false; @@ -158,35 +164,41 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200; private static final long AUTODIM_TIMEOUT_MS = 2250; + private final Context mContext; + private final WindowManager mWindowManager; + private final AccessibilityManager mAccessibilityManager; private final AccessibilityManagerWrapper mAccessibilityManagerWrapper; - protected final AssistManager mAssistManager; - private SysUiState mSysUiFlagsContainer; - private final MetricsLogger mMetricsLogger; private final DeviceProvisionedController mDeviceProvisionedController; private final StatusBarStateController mStatusBarStateController; + private final MetricsLogger mMetricsLogger; + private final Lazy<AssistManager> mAssistManagerLazy; + private final SysUiState mSysUiFlagsContainer; + private final Lazy<StatusBar> mStatusBarLazy; + private final ShadeController mShadeController; + private final NotificationRemoteInputManager mNotificationRemoteInputManager; + private final OverviewProxyService mOverviewProxyService; private final NavigationModeController mNavigationModeController; + private final BroadcastDispatcher mBroadcastDispatcher; + private final CommandQueue mCommandQueue; + private final Divider mDivider; + private final Optional<Recents> mRecentsOptional; + private final SystemActions mSystemActions; + private final Handler mHandler; + private final UiEventLogger mUiEventLogger; - protected NavigationBarView mNavigationBarView = null; + private Bundle mSavedState; + private NavigationBarView mNavigationBarView = null; private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING; private int mNavigationIconHints = 0; private @TransitionMode int mNavigationBarMode; - private AccessibilityManager mAccessibilityManager; private ContentResolver mContentResolver; private boolean mAssistantAvailable; private int mDisabledFlags1; private int mDisabledFlags2; - private final Lazy<StatusBar> mStatusBarLazy; - private final ShadeController mShadeController; - private final NotificationRemoteInputManager mNotificationRemoteInputManager; - private final Divider mDivider; - private final Optional<Recents> mRecentsOptional; - private WindowManager mWindowManager; - private final CommandQueue mCommandQueue; private long mLastLockToAppLongPress; - private final SystemActions mSystemActions; private Locale mLocale; private int mLayoutDirection; @@ -202,10 +214,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback private LightBarController mLightBarController; private AutoHideController mAutoHideController; - private OverviewProxyService mOverviewProxyService; - - private final BroadcastDispatcher mBroadcastDispatcher; - @VisibleForTesting public int mDisplayId; private boolean mIsOnDefaultDisplay; @@ -226,7 +234,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback private int mStartingQuickSwitchRotation = -1; private int mCurrentRotation; private ViewTreeObserver.OnGlobalLayoutListener mOrientationHandleGlobalLayoutListener; - private UiEventLogger mUiEventLogger; private boolean mShowOrientedHandleForImmersiveMode; @com.android.internal.annotations.VisibleForTesting @@ -251,7 +258,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback @Nullable private AssistHandleViewController mAssistHandlerViewController; - private final Handler mHandler; private final AutoHideUiElement mAutoHideUiElement = new AutoHideUiElement() { @Override @@ -307,7 +313,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback @Override public void startAssistant(Bundle bundle) { - mAssistManager.startAssist(bundle); + mAssistManagerLazy.get().startAssist(bundle); } @Override @@ -360,7 +366,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback new Handler(Looper.getMainLooper())) { @Override public void onChange(boolean selfChange, Uri uri) { - boolean available = mAssistManager + boolean available = mAssistManagerLazy.get() .getAssistInfoForUser(UserHandle.USER_CURRENT) != null; if (mAssistantAvailable != available) { sendAssistantAvailability(available); @@ -369,34 +375,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } }; - private static class NavBarViewAttachedListener implements View.OnAttachStateChangeListener { - private NavigationBarFragment mFragment; - private FragmentListener mListener; - - NavBarViewAttachedListener(NavigationBarFragment fragment, FragmentListener listener) { - mFragment = fragment; - mListener = listener; - } - - @Override - public void onViewAttachedToWindow(View v) { - final FragmentHostManager fragmentHost = FragmentHostManager.get(v); - fragmentHost.getFragmentManager().beginTransaction() - .replace(R.id.navigation_bar_frame, mFragment, TAG) - .commit(); - fragmentHost.addTagListener(TAG, mListener); - mFragment = null; - } - - @Override - public void onViewDetachedFromWindow(View v) { - final FragmentHostManager fragmentHost = FragmentHostManager.get(v); - fragmentHost.removeTagListener(TAG, mListener); - FragmentHostManager.removeAndDestroy(v); - v.removeOnAttachStateChangeListener(this); - } - } - private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = new DeviceConfig.OnPropertiesChangedListener() { @Override @@ -416,10 +394,14 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } }; - @Inject - public NavigationBarFragment(AccessibilityManagerWrapper accessibilityManagerWrapper, - DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger, - AssistManager assistManager, OverviewProxyService overviewProxyService, + public NavigationBar(Context context, + WindowManager windowManager, + Lazy<AssistManager> assistManagerLazy, + AccessibilityManager accessibilityManager, + AccessibilityManagerWrapper accessibilityManagerWrapper, + DeviceProvisionedController deviceProvisionedController, + MetricsLogger metricsLogger, + OverviewProxyService overviewProxyService, NavigationModeController navigationModeController, StatusBarStateController statusBarStateController, SysUiState sysUiFlagsContainer, @@ -431,16 +413,18 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback SystemActions systemActions, @Main Handler mainHandler, UiEventLogger uiEventLogger) { + mContext = context; + mWindowManager = windowManager; + mAccessibilityManager = accessibilityManager; mAccessibilityManagerWrapper = accessibilityManagerWrapper; mDeviceProvisionedController = deviceProvisionedController; mStatusBarStateController = statusBarStateController; mMetricsLogger = metricsLogger; - mAssistManager = assistManager; + mAssistManagerLazy = assistManagerLazy; mSysUiFlagsContainer = sysUiFlagsContainer; mStatusBarLazy = statusBarLazy; mShadeController = shadeController; mNotificationRemoteInputManager = notificationRemoteInputManager; - mAssistantAvailable = mAssistManager.getAssistInfoForUser(UserHandle.USER_CURRENT) != null; mOverviewProxyService = overviewProxyService; mNavigationModeController = navigationModeController; mNavBarMode = navigationModeController.addListener(this); @@ -453,25 +437,53 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mUiEventLogger = uiEventLogger; } - // ----- Fragment Lifecycle Callbacks ----- + public View getView() { + return mNavigationBarView; + } - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mCommandQueue.observe(getLifecycle(), this); - mWindowManager = getContext().getSystemService(WindowManager.class); - mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class); - mContentResolver = getContext().getContentResolver(); + public View createView(Bundle savedState) { + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, + WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH + | WindowManager.LayoutParams.FLAG_SLIPPERY, + PixelFormat.TRANSLUCENT); + lp.token = new Binder(); + lp.setTitle("NavigationBar" + mContext.getDisplayId()); + lp.accessibilityTitle = mContext.getString(R.string.nav_bar); + lp.windowAnimations = 0; + lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; + + LayoutInflater layoutInflater = LayoutInflater.from(mContext); + NavigationBarFrame frame = (NavigationBarFrame) layoutInflater.inflate( + R.layout.navigation_bar_window, null); + View barView = layoutInflater.inflate(R.layout.navigation_bar, frame); + barView.addOnAttachStateChangeListener(this); + + if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + barView); + mContext.getSystemService(WindowManager.class).addView(frame, lp); + mDisplayId = mContext.getDisplayId(); + mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY; + + mCommandQueue.addCallback(this); + mAssistantAvailable = mAssistManagerLazy.get() + .getAssistInfoForUser(UserHandle.USER_CURRENT) != null; + mContentResolver = mContext.getContentResolver(); mContentResolver.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.ASSISTANT), false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL); - if (savedInstanceState != null) { - mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0); - mDisabledFlags2 = savedInstanceState.getInt(EXTRA_DISABLE2_STATE, 0); - mAppearance = savedInstanceState.getInt(EXTRA_APPEARANCE, 0); - mTransientShown = savedInstanceState.getBoolean(EXTRA_TRANSIENT_STATE, false); + if (savedState != null) { + mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0); + mDisabledFlags2 = savedState.getInt(EXTRA_DISABLE2_STATE, 0); + mAppearance = savedState.getInt(EXTRA_APPEARANCE, 0); + mTransientShown = savedState.getBoolean(EXTRA_TRANSIENT_STATE, false); } + mSavedState = savedState; mAccessibilityManagerWrapper.addCallback(mAccessibilityListener); // Respect the latest disabled-flags. @@ -486,12 +498,16 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup(); mDeviceProvisionedController.addCallback(mUserSetupListener); + + return barView; } - @Override - public void onDestroy() { - super.onDestroy(); + public void destroyView() { + mCommandQueue.removeCallback(this); + mContext.getSystemService(WindowManager.class).removeViewImmediate( + mNavigationBarView.getRootView()); mNavigationModeController.removeListener(this); + mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener); mContentResolver.unregisterContentObserver(mAssistContentObserver); mDeviceProvisionedController.removeCallback(mUserSetupListener); @@ -500,28 +516,15 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, - Bundle savedInstanceState) { - return inflater.inflate(R.layout.navigation_bar, container, false); - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - mNavigationBarView = (NavigationBarView) view; - final Display display = view.getDisplay(); - // It may not have display when running unit test. - if (display != null) { - mDisplayId = display.getDisplayId(); - mIsOnDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY; - } - + public void onViewAttachedToWindow(View v) { + final Display display = v.getDisplay(); + mNavigationBarView = v.findViewById(R.id.navigation_bar_view); mNavigationBarView.setComponents(mStatusBarLazy.get().getPanelController()); mNavigationBarView.setDisabledFlags(mDisabledFlags1); mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged); mNavigationBarView.setOnTouchListener(this::onNavigationTouch); - if (savedInstanceState != null) { - mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState); + if (mSavedState != null) { + mNavigationBarView.getLightTransitionsController().restoreState(mSavedState); } mNavigationBarView.setNavigationIconHints(mNavigationIconHints); mNavigationBarView.setWindowVisible(isNavBarWindowVisible()); @@ -561,11 +564,32 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } initSecondaryHomeHandleForRotation(); + + // Unfortunately, we still need it because status bar needs LightBarController + // before notifications creation. We cannot directly use getLightBarController() + // from NavigationBarFragment directly. + LightBarController lightBarController = mIsOnDefaultDisplay + ? Dependency.get(LightBarController.class) + : new LightBarController(mContext, + Dependency.get(DarkIconDispatcher.class), + Dependency.get(BatteryController.class), + Dependency.get(NavigationModeController.class)); + setLightBarController(lightBarController); + + // TODO(b/118592525): to support multi-display, we start to add something which is + // per-display, while others may be global. I think it's time to + // add a new class maybe named DisplayDependency to solve + // per-display Dependency problem. + AutoHideController autoHideController = mIsOnDefaultDisplay + ? Dependency.get(AutoHideController.class) + : new AutoHideController(mContext, mHandler, + Dependency.get(IWindowManager.class)); + setAutoHideController(autoHideController); + restoreAppearanceAndTransientState(); } @Override - public void onDestroyView() { - super.onDestroyView(); + public void onViewDetachedFromWindow(View v) { if (mNavigationBarView != null) { if (mIsOnDefaultDisplay) { mNavigationBarView.getBarTransitions() @@ -573,13 +597,13 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mAssistHandlerViewController = null; } mNavigationBarView.getBarTransitions().destroy(); - mNavigationBarView.getLightTransitionsController().destroy(getContext()); + mNavigationBarView.getLightTransitionsController().destroy(mContext); } mOverviewProxyService.removeCallback(mOverviewProxyListener); mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver); if (mOrientationHandle != null) { resetSecondaryHandle(); - getContext().getSystemService(DisplayManager.class).unregisterDisplayListener(this); + mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this); getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener); mWindowManager.removeView(mOrientationHandle); mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener( @@ -590,9 +614,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mOrientationHandle = null; } - @Override + // TODO: Remove this when we update nav bar recreation public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1); outState.putInt(EXTRA_DISABLE2_STATE, mDisabledFlags2); outState.putInt(EXTRA_APPEARANCE, mAppearance); @@ -602,10 +625,11 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } } - @Override + /** + * Called when a non-reloading configuration change happens and we need to update. + */ public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - final Locale locale = getContext().getResources().getConfiguration().locale; + final Locale locale = mContext.getResources().getConfiguration().locale; final int ld = TextUtils.getLayoutDirectionFromLocale(locale); if (!locale.equals(mLocale) || ld != mLayoutDirection) { if (DEBUG) { @@ -625,10 +649,10 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback return; } - getContext().getSystemService(DisplayManager.class) + mContext.getSystemService(DisplayManager.class) .registerDisplayListener(this, new Handler(Looper.getMainLooper())); - mOrientationHandle = new QuickswitchOrientedNavHandle(getContext()); + mOrientationHandle = new QuickswitchOrientedNavHandle(mContext); mOrientationHandle.setId(R.id.secondary_home_handle); getBarTransitions().addDarkIntensityListener(mOrientationHandleIntensityListener); @@ -640,7 +664,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH | WindowManager.LayoutParams.FLAG_SLIPPERY, PixelFormat.TRANSLUCENT); - mOrientationParams.setTitle("SecondaryHomeHandle" + getContext().getDisplayId()); + mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId()); mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION; mWindowManager.addView(mOrientationHandle, mOrientationParams); mOrientationHandle.setVisibility(View.GONE); @@ -729,23 +753,20 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback return delta; } - @Override - public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) { + public void dump(PrintWriter pw) { + pw.println("NavigationBar (displayId=" + mDisplayId + "):"); + pw.println(" mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation); + pw.println(" mCurrentRotation=" + mCurrentRotation); + if (mNavigationBarView != null) { - pw.print(" mNavigationBarWindowState="); - pw.println(windowStateToString(mNavigationBarWindowState)); - pw.print(" mNavigationBarMode="); - pw.println(BarTransitions.modeToString(mNavigationBarMode)); + pw.println(" mNavigationBarWindowState=" + + windowStateToString(mNavigationBarWindowState)); + pw.println(" mNavigationBarMode=" + + BarTransitions.modeToString(mNavigationBarMode)); dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions()); - } - - pw.print(" mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation); - pw.print(" mCurrentRotation=" + mCurrentRotation); - pw.print(" mNavigationBarView="); - if (mNavigationBarView == null) { - pw.println("null"); + mNavigationBarView.dump(pw); } else { - mNavigationBarView.dump(fd, pw, args); + pw.print(" mNavigationBarView=null"); } } @@ -834,15 +855,18 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback rotationButtonController.onRotationProposal(rotation, winRotation, isValid); } - /** Restores the appearance and the transient saved state to {@link NavigationBarFragment}. */ + /** Restores the appearance and the transient saved state to {@link NavigationBar}. */ public void restoreAppearanceAndTransientState() { final int barMode = barMode(mTransientShown, mAppearance); mNavigationBarMode = barMode; checkNavBarModes(); - mAutoHideController.touchAutoHide(); - - mLightBarController.onNavigationBarAppearanceChanged(mAppearance, true /* nbModeChanged */, - barMode, false /* navbarColorManagedByIme */); + if (mAutoHideController != null) { + mAutoHideController.touchAutoHide(); + } + if (mLightBarController != null) { + mLightBarController.onNavigationBarAppearanceChanged(mAppearance, + true /* nbModeChanged */, barMode, false /* navbarColorManagedByIme */); + } } @Override @@ -859,8 +883,10 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } nbModeChanged = updateBarMode(barMode(mTransientShown, appearance)); } - mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged, - mNavigationBarMode, navbarColorManagedByIme); + if (mLightBarController != null) { + mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged, + mNavigationBarMode, navbarColorManagedByIme); + } } @Override @@ -903,7 +929,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mNavigationBarView.onTransientStateChanged(mTransientShown); } final int barMode = barMode(mTransientShown, mAppearance); - if (updateBarMode(barMode)) { + if (updateBarMode(barMode) && mLightBarController != null) { mLightBarController.onNavigationBarModeChanged(barMode); } } @@ -917,7 +943,9 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } mNavigationBarMode = barMode; checkNavBarModes(); - mAutoHideController.touchAutoHide(); + if (mAutoHideController != null) { + mAutoHideController.touchAutoHide(); + } return true; } return false; @@ -1053,7 +1081,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback case MotionEvent.ACTION_DOWN: mHomeBlockedThisTouch = false; TelecomManager telecomManager = - getContext().getSystemService(TelecomManager.class); + mContext.getSystemService(TelecomManager.class); if (telecomManager != null && telecomManager.isRinging()) { if (mStatusBarLazy.get().isKeyguardShowing()) { Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " + @@ -1076,7 +1104,9 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } private boolean onNavigationTouch(View v, MotionEvent event) { - mAutoHideController.checkUserAutoHide(event); + if (mAutoHideController != null) { + mAutoHideController.checkUserAutoHide(event); + } return false; } @@ -1094,7 +1124,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback Bundle args = new Bundle(); args.putInt( AssistManager.INVOCATION_TYPE_KEY, AssistManager.INVOCATION_HOME_BUTTON_LONG_PRESS); - mAssistManager.startAssist(args); + mAssistManagerLazy.get().startAssist(args); mStatusBarLazy.get().awakenDreams(); if (mNavigationBarView != null) { @@ -1120,8 +1150,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } private void onRecentsClick(View v) { - if (LatencyTracker.isEnabled(getContext())) { - LatencyTracker.getInstance(getContext()).onActionStart( + if (LatencyTracker.isEnabled(mContext)) { + LatencyTracker.getInstance(mContext).onActionStart( LatencyTracker.ACTION_TOGGLE_RECENTS); } mStatusBarLazy.get().awakenDreams(); @@ -1215,7 +1245,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } private boolean onLongPressRecents() { - if (mRecentsOptional.isPresent() || !ActivityTaskManager.supportsMultiWindow(getContext()) + if (mRecentsOptional.isPresent() || !ActivityTaskManager.supportsMultiWindow(mContext) || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible() || ActivityManager.isLowRamDeviceStatic() // If we are connected to the overview service, then disable the recents button @@ -1230,7 +1260,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback private void onAccessibilityClick(View v) { final Display display = v.getDisplay(); mAccessibilityManager.notifyAccessibilityButtonClicked( - display != null ? display.getDisplayId() : Display.DEFAULT_DISPLAY); + display != null ? display.getDisplayId() : DEFAULT_DISPLAY); } private boolean onAccessibilityLongClick(View v) { @@ -1238,7 +1268,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); final String chooserClassName = AccessibilityButtonChooserActivity.class.getName(); intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName); - v.getContext().startActivityAsUser(intent, UserHandle.CURRENT); + mContext.startActivityAsUser(intent, UserHandle.CURRENT); return true; } @@ -1335,7 +1365,10 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback public void setLightBarController(LightBarController lightBarController) { mLightBarController = lightBarController; - mLightBarController.setNavigationBar(mNavigationBarView.getLightTransitionsController()); + if (mLightBarController != null) { + mLightBarController.setNavigationBar( + mNavigationBarView.getLightTransitionsController()); + } } /** Sets {@link AutoHideController} to the navigation bar. */ @@ -1381,17 +1414,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback if (!canShowSecondaryHandle()) { resetSecondaryHandle(); } - - // Workaround for b/132825155, for secondary users, we currently don't receive configuration - // changes on overlay package change since SystemUI runs for the system user. In this case, - // trigger a new configuration change to ensure that the nav bar is updated in the same way. - int userId = ActivityManagerWrapper.getInstance().getCurrentUserId(); - if (userId != UserHandle.USER_SYSTEM) { - mHandler.post(() -> { - FragmentHostManager fragmentHost = FragmentHostManager.get(mNavigationBarView); - fragmentHost.reloadFragments(); - }); - } } public void disableAnimationsDuringHide(long delay) { @@ -1442,7 +1464,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback return; } - int rotation = getContext().getResources().getConfiguration() + int rotation = mContext.getResources().getConfiguration() .windowConfiguration.getRotation(); if (rotation != mCurrentRotation) { mCurrentRotation = rotation; @@ -1480,37 +1502,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } }; - public static View create(Context context, FragmentListener listener) { - WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, - WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH - | WindowManager.LayoutParams.FLAG_SLIPPERY, - PixelFormat.TRANSLUCENT); - lp.token = new Binder(); - lp.setTitle("NavigationBar" + context.getDisplayId()); - lp.accessibilityTitle = context.getString(R.string.nav_bar); - lp.windowAnimations = 0; - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; - - View navigationBarView = LayoutInflater.from(context).inflate( - R.layout.navigation_bar_window, null); - - if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView); - if (navigationBarView == null) return null; - - NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView) - .create(NavigationBarFragment.class); - navigationBarView.addOnAttachStateChangeListener(new NavBarViewAttachedListener(fragment, - listener)); - context.getSystemService(WindowManager.class).addView(navigationBarView, lp); - return navigationBarView; - } - @VisibleForTesting int getNavigationIconHints() { return mNavigationIconHints; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java new file mode 100644 index 000000000000..fd157c632c7c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.navigationbar; + +import static android.view.Display.DEFAULT_DISPLAY; + +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.hardware.display.DisplayManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; +import android.util.SparseArray; +import android.view.Display; +import android.view.IWindowManager; +import android.view.View; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; +import android.view.accessibility.AccessibilityManager; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.statusbar.RegisterStatusBarResult; +import com.android.settingslib.applications.InterestingConfigChanges; +import com.android.systemui.Dumpable; +import com.android.systemui.accessibility.SystemActions; +import com.android.systemui.assist.AssistHandleViewController; +import com.android.systemui.assist.AssistManager; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.model.SysUiState; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.recents.OverviewProxyService; +import com.android.systemui.recents.Recents; +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.stackdivider.Divider; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.CommandQueue.Callbacks; +import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode; +import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Optional; + +import javax.inject.Inject; + +import dagger.Lazy; + + +/** A controller to handle navigation bars. */ +@SysUISingleton +public class NavigationBarController implements Callbacks, + ConfigurationController.ConfigurationListener, + NavigationModeController.ModeChangedListener, Dumpable { + + private static final String TAG = NavigationBarController.class.getSimpleName(); + + private final Context mContext; + private final WindowManager mWindowManager; + private final Lazy<AssistManager> mAssistManagerLazy; + private final AccessibilityManager mAccessibilityManager; + private final AccessibilityManagerWrapper mAccessibilityManagerWrapper; + private final DeviceProvisionedController mDeviceProvisionedController; + private final MetricsLogger mMetricsLogger; + private final OverviewProxyService mOverviewProxyService; + private final NavigationModeController mNavigationModeController; + private final StatusBarStateController mStatusBarStateController; + private final SysUiState mSysUiFlagsContainer; + private final BroadcastDispatcher mBroadcastDispatcher; + private final CommandQueue mCommandQueue; + private final Divider mDivider; + private final Optional<Recents> mRecentsOptional; + private final Lazy<StatusBar> mStatusBarLazy; + private final ShadeController mShadeController; + private final NotificationRemoteInputManager mNotificationRemoteInputManager; + private final SystemActions mSystemActions; + private final UiEventLogger mUiEventLogger; + private final Handler mHandler; + private final DisplayManager mDisplayManager; + + /** A displayId - nav bar maps. */ + @VisibleForTesting + SparseArray<NavigationBar> mNavigationBars = new SparseArray<>(); + + // Tracks config changes that will actually recreate the nav bar + private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges( + ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE + | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS + | ActivityInfo.CONFIG_UI_MODE); + + @Inject + public NavigationBarController(Context context, + WindowManager windowManager, + Lazy<AssistManager> assistManagerLazy, + AccessibilityManager accessibilityManager, + AccessibilityManagerWrapper accessibilityManagerWrapper, + DeviceProvisionedController deviceProvisionedController, + MetricsLogger metricsLogger, + OverviewProxyService overviewProxyService, + NavigationModeController navigationModeController, + StatusBarStateController statusBarStateController, + SysUiState sysUiFlagsContainer, + BroadcastDispatcher broadcastDispatcher, + CommandQueue commandQueue, + Divider divider, + Optional<Recents> recentsOptional, + Lazy<StatusBar> statusBarLazy, + ShadeController shadeController, + NotificationRemoteInputManager notificationRemoteInputManager, + SystemActions systemActions, + @Main Handler mainHandler, + UiEventLogger uiEventLogger, + ConfigurationController configurationController) { + mContext = context; + mWindowManager = windowManager; + mAssistManagerLazy = assistManagerLazy; + mAccessibilityManager = accessibilityManager; + mAccessibilityManagerWrapper = accessibilityManagerWrapper; + mDeviceProvisionedController = deviceProvisionedController; + mMetricsLogger = metricsLogger; + mOverviewProxyService = overviewProxyService; + mNavigationModeController = navigationModeController; + mStatusBarStateController = statusBarStateController; + mSysUiFlagsContainer = sysUiFlagsContainer; + mBroadcastDispatcher = broadcastDispatcher; + mCommandQueue = commandQueue; + mDivider = divider; + mRecentsOptional = recentsOptional; + mStatusBarLazy = statusBarLazy; + mShadeController = shadeController; + mNotificationRemoteInputManager = notificationRemoteInputManager; + mSystemActions = systemActions; + mUiEventLogger = uiEventLogger; + mHandler = mainHandler; + mDisplayManager = mContext.getSystemService(DisplayManager.class); + commandQueue.addCallback(this); + configurationController.addCallback(this); + mConfigChanges.applyNewConfig(mContext.getResources()); + } + + @Override + public void onConfigChanged(Configuration newConfig) { + if (mConfigChanges.applyNewConfig(mContext.getResources())) { + for (int i = 0; i < mNavigationBars.size(); i++) { + recreateNavigationBar(mNavigationBars.keyAt(i)); + } + } else { + for (int i = 0; i < mNavigationBars.size(); i++) { + mNavigationBars.get(i).onConfigurationChanged(newConfig); + } + } + } + + @Override + public void onNavigationModeChanged(int mode) { + // Workaround for b/132825155, for secondary users, we currently don't receive configuration + // changes on overlay package change since SystemUI runs for the system user. In this case, + // trigger a new configuration change to ensure that the nav bar is updated in the same way. + int userId = ActivityManagerWrapper.getInstance().getCurrentUserId(); + if (userId != UserHandle.USER_SYSTEM) { + mHandler.post(() -> { + for (int i = 0; i < mNavigationBars.size(); i++) { + recreateNavigationBar(mNavigationBars.keyAt(i)); + } + }); + } + } + + @Override + public void onDisplayRemoved(int displayId) { + removeNavigationBar(displayId); + } + + @Override + public void onDisplayReady(int displayId) { + Display display = mDisplayManager.getDisplay(displayId); + createNavigationBar(display, null /* savedState */, null /* result */); + } + + /** + * Recreates the navigation bar for the given display. + */ + private void recreateNavigationBar(int displayId) { + // TODO: Improve this flow so that we don't need to create a new nav bar but just + // the view + Bundle savedState = new Bundle(); + NavigationBar bar = mNavigationBars.get(displayId); + if (bar != null) { + bar.onSaveInstanceState(savedState); + } + removeNavigationBar(displayId); + createNavigationBar(mDisplayManager.getDisplay(displayId), savedState, null /* result */); + } + + // TODO(b/117478341): I use {@code includeDefaultDisplay} to make this method compatible to + // CarStatusBar because they have their own nav bar. Think about a better way for it. + /** + * Creates navigation bars when car/status bar initializes. + * + * @param includeDefaultDisplay {@code true} to create navigation bar on default display. + */ + public void createNavigationBars(final boolean includeDefaultDisplay, + RegisterStatusBarResult result) { + Display[] displays = mDisplayManager.getDisplays(); + for (Display display : displays) { + if (includeDefaultDisplay || display.getDisplayId() != DEFAULT_DISPLAY) { + createNavigationBar(display, null /* savedState */, result); + } + } + } + + /** + * Adds a navigation bar on default display or an external display if the display supports + * system decorations. + * + * @param display the display to add navigation bar on. + */ + @VisibleForTesting + void createNavigationBar(Display display, Bundle savedState, RegisterStatusBarResult result) { + if (display == null) { + return; + } + + final int displayId = display.getDisplayId(); + final boolean isOnDefaultDisplay = displayId == DEFAULT_DISPLAY; + final IWindowManager wms = WindowManagerGlobal.getWindowManagerService(); + + try { + if (!wms.hasNavigationBar(displayId)) { + return; + } + } catch (RemoteException e) { + // Cannot get wms, just return with warning message. + Log.w(TAG, "Cannot get WindowManager."); + return; + } + final Context context = isOnDefaultDisplay + ? mContext + : mContext.createDisplayContext(display); + NavigationBar navBar = new NavigationBar(context, + mWindowManager, + mAssistManagerLazy, + mAccessibilityManager, + mAccessibilityManagerWrapper, + mDeviceProvisionedController, + mMetricsLogger, + mOverviewProxyService, + mNavigationModeController, + mStatusBarStateController, + mSysUiFlagsContainer, + mBroadcastDispatcher, + mCommandQueue, + mDivider, + mRecentsOptional, + mStatusBarLazy, + mShadeController, + mNotificationRemoteInputManager, + mSystemActions, + mHandler, + mUiEventLogger); + + View navigationBarView = navBar.createView(savedState); + navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + mNavigationBars.put(displayId, navBar); + + if (result != null) { + navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken, + result.mImeWindowVis, result.mImeBackDisposition, + result.mShowImeSwitcher); + } + } + + @Override + public void onViewDetachedFromWindow(View v) { + v.removeOnAttachStateChangeListener(this); + } + }); + } + + private void removeNavigationBar(int displayId) { + NavigationBar navBar = mNavigationBars.get(displayId); + if (navBar != null) { + navBar.setAutoHideController(/* autoHideController */ null); + navBar.destroyView(); + mNavigationBars.remove(displayId); + } + } + + /** @see NavigationBar#checkNavBarModes() */ + public void checkNavBarModes(int displayId) { + NavigationBar navBar = mNavigationBars.get(displayId); + if (navBar != null) { + navBar.checkNavBarModes(); + } + } + + /** @see NavigationBar#finishBarAnimations() */ + public void finishBarAnimations(int displayId) { + NavigationBar navBar = mNavigationBars.get(displayId); + if (navBar != null) { + navBar.finishBarAnimations(); + } + } + + /** @see NavigationBar#touchAutoDim() */ + public void touchAutoDim(int displayId) { + NavigationBar navBar = mNavigationBars.get(displayId); + if (navBar != null) { + navBar.touchAutoDim(); + } + } + + /** @see NavigationBar#transitionTo(int, boolean) */ + public void transitionTo(int displayId, @TransitionMode int barMode, boolean animate) { + NavigationBar navBar = mNavigationBars.get(displayId); + if (navBar != null) { + navBar.transitionTo(barMode, animate); + } + } + + /** @see NavigationBar#disableAnimationsDuringHide(long) */ + public void disableAnimationsDuringHide(int displayId, long delay) { + NavigationBar navBar = mNavigationBars.get(displayId); + if (navBar != null) { + navBar.disableAnimationsDuringHide(delay); + } + } + + /** @return {@link NavigationBarView} on the default display. */ + public @Nullable NavigationBarView getDefaultNavigationBarView() { + return getNavigationBarView(DEFAULT_DISPLAY); + } + + /** + * @param displayId the ID of display which Navigation bar is on + * @return {@link NavigationBarView} on the display with {@code displayId}. + * {@code null} if no navigation bar on that display. + */ + public @Nullable NavigationBarView getNavigationBarView(int displayId) { + NavigationBar navBar = mNavigationBars.get(displayId); + return (navBar == null) ? null : (NavigationBarView) navBar.getView(); + } + + /** @return {@link NavigationBar} on the default display. */ + @Nullable + public NavigationBar getDefaultNavigationBar() { + return mNavigationBars.get(DEFAULT_DISPLAY); + } + + /** @return {@link AssistHandleViewController} (only on the default display). */ + @Nullable + public AssistHandleViewController getAssistHandlerViewController() { + NavigationBar navBar = getDefaultNavigationBar(); + return navBar == null ? null : navBar.getAssistHandlerViewController(); + } + + @Override + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + for (int i = 0; i < mNavigationBars.size(); i++) { + if (i > 0) { + pw.println(); + } + mNavigationBars.get(i).dump(pw); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFrame.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarFrame.java index 741f7839f455..6c531d8233f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFrame.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarFrame.java @@ -1,18 +1,20 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2020 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 + * 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. + * 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 com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar; import static android.view.MotionEvent.ACTION_OUTSIDE; @@ -24,7 +26,7 @@ import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.FrameLayout; -import com.android.systemui.statusbar.policy.DeadZone; +import com.android.systemui.navigationbar.buttons.DeadZone; public class NavigationBarFrame extends FrameLayout { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java index 4337e20c0a39..27074606d251 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java @@ -1,18 +1,20 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2020 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 + * 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. + * 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 com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; @@ -35,10 +37,12 @@ import android.widget.Space; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.navigationbar.buttons.ButtonDispatcher; +import com.android.systemui.navigationbar.buttons.KeyButtonView; +import com.android.systemui.navigationbar.buttons.ReverseLinearLayout; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.statusbar.phone.ReverseLinearLayout.ReverseRelativeLayout; -import com.android.systemui.statusbar.policy.KeyButtonView; +import com.android.systemui.navigationbar.buttons.ReverseLinearLayout.ReverseRelativeLayout; import java.io.PrintWriter; import java.util.Objects; @@ -472,8 +476,7 @@ public class NavigationBarInflaterView extends FrameLayout } public void dump(PrintWriter pw) { - pw.println("NavigationBarInflaterView {"); - pw.println(" mCurrentLayout: " + mCurrentLayout); - pw.println(" }"); + pw.println("NavigationBarInflaterView"); + pw.println(" mCurrentLayout: " + mCurrentLayout); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java index 1f509549efd1..c0535b5fc77b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,27 +14,27 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.systemui.util.Utils.isGesturalModeOnDefaultDisplay; -import android.content.Context; import android.graphics.Rect; import android.os.Handler; import android.os.RemoteException; -import android.os.ServiceManager; import android.util.SparseArray; import android.view.Display; import android.view.IWallpaperVisibilityListener; import android.view.IWindowManager; import android.view.View; -import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.navigationbar.buttons.ButtonDispatcher; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.phone.BarTransitions; +import com.android.systemui.statusbar.phone.LightBarTransitionsController; import java.util.ArrayList; import java.util.List; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index e3cb105ba2f7..a3351838ecf2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; @@ -70,6 +70,15 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.assist.AssistHandleViewController; import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.buttons.ButtonDispatcher; +import com.android.systemui.navigationbar.buttons.ContextualButton; +import com.android.systemui.navigationbar.buttons.ContextualButtonGroup; +import com.android.systemui.navigationbar.buttons.DeadZone; +import com.android.systemui.navigationbar.buttons.KeyButtonDrawable; +import com.android.systemui.navigationbar.buttons.RotationContextButton; +import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; +import com.android.systemui.navigationbar.gestural.FloatingRotationButton; +import com.android.systemui.navigationbar.gestural.RegionSamplingHelper; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsOnboarding; @@ -80,9 +89,10 @@ import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.NavigationBarController; -import com.android.systemui.statusbar.policy.DeadZone; -import com.android.systemui.statusbar.policy.KeyButtonDrawable; +import com.android.systemui.statusbar.phone.AutoHideController; +import com.android.systemui.statusbar.phone.LightBarTransitionsController; +import com.android.systemui.statusbar.phone.NotificationPanelViewController; +import com.android.systemui.statusbar.phone.StatusBar; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -167,7 +177,7 @@ public class NavigationBarView extends FrameLayout implements * When quickswitching between apps of different orientations, we draw a secondary home handle * in the position of the first app's orientation. This rect represents the region of that * home handle so we can apply the correct light/dark luma on that. - * @see {@link NavigationBarFragment#mOrientationHandle} + * @see {@link NavigationBar#mOrientationHandle} */ @Nullable private Rect mOrientedHandleSamplingRegion; @@ -779,7 +789,7 @@ public class NavigationBarView extends FrameLayout implements } else { return; } - WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE); + WindowManager wm = getContext().getSystemService(WindowManager.class); wm.updateViewLayout((View) getParent(), lp); } } @@ -867,7 +877,7 @@ public class NavigationBarView extends FrameLayout implements } else { lp.flags &= ~flags; } - WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); + WindowManager wm = getContext().getSystemService(WindowManager.class); wm.updateViewLayout(navbarView, lp); } @@ -1163,6 +1173,9 @@ public class NavigationBarView extends FrameLayout implements @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + // This needs to happen first as it can changed the enabled state which can affect whether + // the back button is visible + mEdgeBackGestureHandler.onNavBarAttached(); requestApplyInsets(); reorient(); onNavigationModeChanged(mNavBarMode); @@ -1171,7 +1184,6 @@ public class NavigationBarView extends FrameLayout implements mRotationButtonController.registerListeners(); } - mEdgeBackGestureHandler.onNavBarAttached(); getViewTreeObserver().addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener); } @@ -1200,12 +1212,12 @@ public class NavigationBarView extends FrameLayout implements } } - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("NavigationBarView {"); + public void dump(PrintWriter pw) { final Rect r = new Rect(); final Point size = new Point(); getContextDisplay().getRealSize(size); + pw.println("NavigationBarView:"); pw.println(String.format(" this: " + StatusBar.viewInfo(this) + " " + visibilityToString(getVisibility()))); @@ -1228,6 +1240,8 @@ public class NavigationBarView extends FrameLayout implements getLightTransitionsController().getCurrentDarkIntensity())); pw.println(" mOrientedHandleSamplingRegion: " + mOrientedHandleSamplingRegion); + pw.println(" mScreenOn: " + mScreenOn); + dumpButton(pw, "back", getBackButton()); dumpButton(pw, "home", getHomeButton()); @@ -1236,9 +1250,6 @@ public class NavigationBarView extends FrameLayout implements dumpButton(pw, "a11y", getAccessibilityButton()); dumpButton(pw, "ime", getImeSwitchButton()); - pw.println(" }"); - pw.println(" mScreenOn: " + mScreenOn); - if (mNavigationInflaterView != null) { mNavigationInflaterView.dump(pw); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java index c211de08cb8e..c704d32a4233 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar; import static android.content.Intent.ACTION_OVERLAY_CHANGED; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; @@ -37,6 +37,7 @@ import android.provider.Settings.Secure; import android.util.Log; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -48,12 +49,11 @@ import java.util.ArrayList; import java.util.concurrent.Executor; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controller for tracking the current navigation bar mode. */ -@Singleton +@SysUISingleton public class NavigationModeController implements Dumpable { private static final String TAG = NavigationModeController.class.getSimpleName(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java index 74d4eb175ac4..e48785844347 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButton.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,16 +14,16 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar; import android.view.View; -import com.android.systemui.statusbar.policy.KeyButtonDrawable; +import com.android.systemui.navigationbar.buttons.KeyButtonDrawable; import java.util.function.Consumer; /** Interface of a rotation button that interacts {@link RotationButtonController}. */ -interface RotationButton { +public interface RotationButton { void setRotationButtonController(RotationButtonController rotationButtonController); void setVisibilityChangedCallback(Consumer<Boolean> visibilityChangedCallback); View getCurrentView(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java index 2f2e1f9a1f2e..6cbf065ea6a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar; import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION; @@ -23,7 +23,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.annotation.ColorInt; import android.annotation.DrawableRes; -import android.annotation.StyleRes; import android.app.StatusBarManager; import android.content.ContentResolver; import android.content.Context; @@ -32,7 +31,6 @@ import android.os.Looper; import android.os.RemoteException; import android.provider.Settings; import android.util.Log; -import android.view.ContextThemeWrapper; import android.view.IRotationWatcher.Stub; import android.view.MotionEvent; import android.view.Surface; @@ -43,14 +41,13 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; -import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.navigationbar.buttons.KeyButtonDrawable; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; -import com.android.systemui.statusbar.policy.KeyButtonDrawable; import com.android.systemui.statusbar.policy.RotationLockController; import java.util.Optional; @@ -328,7 +325,7 @@ public class RotationButtonController { } } - Context getContext() { + public Context getContext() { return mContext; } @@ -336,15 +333,15 @@ public class RotationButtonController { return mRotationButton; } - @DrawableRes int getIconResId() { + public @DrawableRes int getIconResId() { return mIconResId; } - @ColorInt int getLightIconColor() { + public @ColorInt int getLightIconColor() { return mLightIconColor; } - @ColorInt int getDarkIconColor() { + public @ColorInt int getDarkIconColor() { return mDarkIconColor; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java b/packages/SystemUI/src/com/android/systemui/navigationbar/ScreenPinningNotify.java index 071e00d08d67..ac7baf592599 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/ScreenPinningNotify.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar; import android.content.Context; import android.os.SystemClock; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java index c2731089132c..ade2923acdf6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java @@ -1,18 +1,20 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2020 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 + * 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. + * 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 com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar.buttons; import static com.android.systemui.Interpolators.LINEAR; @@ -24,7 +26,6 @@ import android.view.View.AccessibilityDelegate; import com.android.systemui.Dependency; import com.android.systemui.assist.AssistManager; -import com.android.systemui.statusbar.policy.KeyButtonDrawable; import java.util.ArrayList; @@ -75,11 +76,11 @@ public class ButtonDispatcher { mAssistManager = Dependency.get(AssistManager.class); } - void clear() { + public void clear() { mViews.clear(); } - void addView(View view) { + public void addView(View view) { mViews.add(view); view.setOnClickListener(mClickListener); view.setOnTouchListener(mTouchListener); @@ -337,6 +338,6 @@ public class ButtonDispatcher { /** * Executes when button is detached from window. */ - protected void onDestroy() { + public void onDestroy() { } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonInterface.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonInterface.java index 150a9603a124..8d291ddf5f19 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonInterface.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonInterface.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 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. @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar.buttons; import android.annotation.Nullable; import android.graphics.drawable.Drawable; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java index eb476457dcec..453e85ae25c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 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. @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar.buttons; import android.annotation.DrawableRes; import android.annotation.IdRes; @@ -22,9 +22,6 @@ import android.annotation.NonNull; import android.content.Context; import android.view.View; -import com.android.systemui.statusbar.policy.KeyButtonDrawable; -import com.android.systemui.statusbar.policy.KeyButtonView; - /** * Simple contextual button that is added to the {@link ContextualButtonGroup}. Extend if need extra * functionality. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButtonGroup.java index c1017f4def0f..50b638bcc903 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButtonGroup.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 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. @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar.buttons; import android.annotation.IdRes; import android.annotation.NonNull; @@ -119,21 +119,20 @@ public class ContextualButtonGroup extends ButtonDispatcher { public void dump(PrintWriter pw) { View view = getCurrentView(); - pw.println("ContextualButtonGroup {"); - pw.println(" getVisibleContextButton(): " + getVisibleContextButton()); - pw.println(" isVisible(): " + isVisible()); - pw.println(" attached(): " + (view != null && view.isAttachedToWindow())); - pw.println(" mButtonData [ "); + pw.println("ContextualButtonGroup"); + pw.println(" getVisibleContextButton(): " + getVisibleContextButton()); + pw.println(" isVisible(): " + isVisible()); + pw.println(" attached(): " + (view != null && view.isAttachedToWindow())); + pw.println(" mButtonData [ "); for (int i = mButtonData.size() - 1; i >= 0; --i) { final ButtonData data = mButtonData.get(i); view = data.button.getCurrentView(); - pw.println(" " + i + ": markedVisible=" + data.markedVisible + pw.println(" " + i + ": markedVisible=" + data.markedVisible + " visible=" + data.button.getVisibility() + " attached=" + (view != null && view.isAttachedToWindow()) + " alpha=" + data.button.getAlpha()); } - pw.println(" ]"); - pw.println(" }"); + pw.println(" ]"); } private int getContextButtonIndex(@IdRes int buttonResId) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java index 12d0617d90ff..7e5b5548237b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.navigationbar.buttons; import android.animation.ObjectAnimator; import android.content.res.Resources; @@ -26,8 +26,8 @@ import android.view.Surface; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.statusbar.NavigationBarController; -import com.android.systemui.statusbar.phone.NavigationBarView; +import com.android.systemui.navigationbar.NavigationBarController; +import com.android.systemui.navigationbar.NavigationBarView; /** * The "dead zone" consumes unintentional taps along the top edge of the navigation bar. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonDrawable.java index 755938863b5e..fc2016913292 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonDrawable.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2020 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. @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.navigationbar.buttons; import android.animation.ArgbEvaluator; import android.annotation.ColorInt; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java index 2d8784dc41bd..72cd4f1343e6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2020 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. @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.navigationbar.buttons; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java index 8d7ecd09e760..d6b831640326 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.navigationbar.buttons; import static android.view.Display.INVALID_DISPLAY; import static android.view.KeyEvent.KEYCODE_UNKNOWN; @@ -61,7 +61,6 @@ import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.statusbar.phone.ButtonInterface; public class KeyButtonView extends ImageView implements ButtonInterface { private static final String TAG = KeyButtonView.class.getSimpleName(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java index d02280836fb5..88c8fea085fb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java @@ -1,18 +1,20 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2020 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 + * 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. + * 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 com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar.buttons; import android.content.Context; import android.content.res.Configuration; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ReverseLinearLayout.java index d3ec187ef20f..f1e1366404a2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ReverseLinearLayout.java @@ -12,7 +12,7 @@ * permissions and limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar.buttons; import android.annotation.Nullable; import android.content.Context; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java index d63d445d8ba3..6a97a3379939 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 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. @@ -11,17 +11,20 @@ * 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 + * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar.buttons; import android.annotation.DrawableRes; import android.annotation.IdRes; import android.content.Context; import android.view.View; -import com.android.systemui.statusbar.policy.KeyButtonDrawable; +import com.android.systemui.navigationbar.RotationButton; +import com.android.systemui.navigationbar.RotationButtonController; +import com.android.systemui.navigationbar.buttons.ContextualButton; +import com.android.systemui.navigationbar.buttons.KeyButtonDrawable; import java.util.function.Consumer; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index 9606318e1992..a1b55c428029 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -1,5 +1,5 @@ -/** - * Copyright (C) 2019 The Android Open Source Project +/* + * Copyright (C) 2020 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. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar.gestural; import static android.view.Display.INVALID_DISPLAY; @@ -59,6 +59,8 @@ import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.NavigationBarView; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.NavigationEdgeBackPlugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.recents.OverviewProxyService; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java index 8a85f7d6a2c5..61118c5d26ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar.gestural; import android.content.Context; import android.content.res.Resources; @@ -27,8 +27,10 @@ import android.view.View; import android.view.WindowManager; import com.android.systemui.R; -import com.android.systemui.statusbar.policy.KeyButtonDrawable; -import com.android.systemui.statusbar.policy.KeyButtonView; +import com.android.systemui.navigationbar.RotationButton; +import com.android.systemui.navigationbar.RotationButtonController; +import com.android.systemui.navigationbar.buttons.KeyButtonDrawable; +import com.android.systemui.navigationbar.buttons.KeyButtonView; import java.util.function.Consumer; @@ -49,7 +51,7 @@ public class FloatingRotationButton implements RotationButton { private RotationButtonController mRotationButtonController; private Consumer<Boolean> mVisibilityChangedCallback; - FloatingRotationButton(Context context) { + public FloatingRotationButton(Context context) { mContext = context; mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); mKeyButtonView = (KeyButtonView) LayoutInflater.from(mContext).inflate( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java index 23573095e037..284f41a416d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar.gestural; import android.animation.ValueAnimator; import android.content.Context; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java index b87479505d00..33e6aa46724b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar.gestural; import android.animation.ArgbEvaluator; import android.annotation.ColorInt; @@ -29,6 +29,7 @@ import android.view.View; import com.android.settingslib.Utils; import com.android.systemui.R; +import com.android.systemui.navigationbar.buttons.ButtonInterface; public class NavigationHandle extends View implements ButtonInterface { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickswitchOrientedNavHandle.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/QuickswitchOrientedNavHandle.java index fe74677a8d51..71c8a2c1e6ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickswitchOrientedNavHandle.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/QuickswitchOrientedNavHandle.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar.gestural; import android.content.Context; import android.graphics.Canvas; @@ -34,7 +34,7 @@ public class QuickswitchOrientedNavHandle extends NavigationHandle { mWidth = context.getResources().getDimensionPixelSize(R.dimen.navigation_home_handle_width); } - void setDeltaRotation(@Surface.Rotation int rotation) { + public void setDeltaRotation(@Surface.Rotation int rotation) { mDeltaRotation = rotation; } @@ -43,7 +43,7 @@ public class QuickswitchOrientedNavHandle extends NavigationHandle { canvas.drawRoundRect(computeHomeHandleBounds(), mRadius, mRadius, mPaint); } - RectF computeHomeHandleBounds() { + public RectF computeHomeHandleBounds() { int left; int top; int bottom; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/RegionSamplingHelper.java index 3c8aa86dd209..70117eb6d2f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/RegionSamplingHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 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. @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar.gestural; import static android.view.Display.DEFAULT_DISPLAY; @@ -108,7 +108,7 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, } } - void start(Rect initialSamplingBounds) { + public void start(Rect initialSamplingBounds) { if (!mCallback.isSamplingEnabled()) { return; } @@ -122,12 +122,12 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, updateSamplingListener(); } - void stop() { + public void stop() { mSamplingEnabled = false; updateSamplingListener(); } - void stopAndDestroy() { + public void stopAndDestroy() { stop(); mSamplingListener.destroy(); mIsDestroyed = true; @@ -220,12 +220,12 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, } } - void setWindowVisible(boolean visible) { + public void setWindowVisible(boolean visible) { mWindowVisible = visible; updateSamplingListener(); } - void dump(PrintWriter pw) { + public void dump(PrintWriter pw) { pw.println("RegionSamplingHelper:"); pw.println(" sampleView isAttached: " + mSampledView.isAttachedToWindow()); pw.println(" sampleView isScValid: " + (mSampledView.isAttachedToWindow() diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java index 2b07ac3f4d8a..9be1b5a35be6 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java @@ -19,6 +19,7 @@ package com.android.systemui.onehanded; import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.IntDef; +import android.content.Context; import android.graphics.Rect; import android.view.SurfaceControl; import android.view.animation.Interpolator; @@ -32,8 +33,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import javax.inject.Inject; - /** * Controller class of OneHanded animations (both from and to OneHanded mode). */ @@ -62,10 +61,8 @@ public class OneHandedAnimationController { /** * Constructor of OneHandedAnimationController */ - @Inject - public OneHandedAnimationController( - OneHandedSurfaceTransactionHelper surfaceTransactionHelper) { - mSurfaceTransactionHelper = surfaceTransactionHelper; + public OneHandedAnimationController(Context context) { + mSurfaceTransactionHelper = new OneHandedSurfaceTransactionHelper(context); mOvershootInterpolator = new OvershootInterpolator(); } diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java index 8550959aa2c4..ad9f7ea4e945 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java @@ -26,6 +26,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.os.Handler; import android.os.Looper; +import android.os.SystemProperties; import android.util.Log; import android.view.SurfaceControl; import android.window.DisplayAreaInfo; @@ -47,8 +48,6 @@ import java.util.HashMap; import java.util.List; import java.util.Objects; -import javax.inject.Inject; - /** * Manages OneHanded display areas such as offset. * @@ -61,6 +60,8 @@ import javax.inject.Inject; */ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implements Dumpable { private static final String TAG = "OneHandedDisplayAreaOrganizer"; + private static final String ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION = + "persist.debug.one_handed_translate_animation_duration"; @VisibleForTesting static final int MSG_RESET_IMMEDIATE = 1; @@ -146,7 +147,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen /** * Constructor of OneHandedDisplayAreaOrganizer */ - @Inject public OneHandedDisplayAreaOrganizer(Context context, DisplayController displayController, OneHandedAnimationController animationController, @@ -156,8 +156,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen mDisplayController = displayController; mDefaultDisplayBounds.set(getDisplayBounds()); mLastVisualDisplayBounds.set(getDisplayBounds()); - mEnterExitAnimationDurationMs = context.getResources().getInteger( - com.android.systemui.R.integer.config_one_handed_translate_animation_duration); + mEnterExitAnimationDurationMs = + SystemProperties.getInt(ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION, 300); mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mTutorialHandler = tutorialHandler; } diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java index 563684ad65c1..1420811b9b30 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java @@ -40,18 +40,14 @@ import android.window.WindowContainerTransaction; import androidx.annotation.VisibleForTesting; import com.android.systemui.R; -import com.android.systemui.statusbar.phone.NavigationModeController; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; -import javax.inject.Inject; -import javax.inject.Singleton; - /** * The class manage swipe up and down gesture for 3-Button mode navigation, * others(e.g, 2-button, full gesture mode) are handled by Launcher quick steps. */ -@Singleton public class OneHandedGestureHandler implements OneHandedTransitionCallback, NavigationModeController.ModeChangedListener, DisplayChangeController.OnDisplayChangingListener { @@ -91,7 +87,6 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, * @param displayController {@link DisplayController} * @param navigationModeController {@link NavigationModeController} */ - @Inject public OneHandedGestureHandler(Context context, DisplayController displayController, NavigationModeController navigationModeController) { mDisplayController = displayController; diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java index a3921ee54fec..90e7e12b2b47 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java @@ -24,14 +24,16 @@ import android.content.ComponentName; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; +import android.os.SystemProperties; import android.view.KeyEvent; import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dumpable; -import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.statusbar.CommandQueue; @@ -42,14 +44,15 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Singleton; /** * Manages and manipulates the one handed states, transitions, and gesture for phones. */ -@Singleton +@SysUISingleton public class OneHandedManagerImpl implements OneHandedManager, Dumpable { private static final String TAG = "OneHandedManager"; + private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE = + "persist.debug.one_handed_offset_percentage"; private boolean mIsOneHandedEnabled; private boolean mIsSwipeToNotificationEnabled; @@ -92,7 +95,6 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { /** * Handle rotation based on OnDisplayChangingListener callback */ - @VisibleForTesting private final DisplayChangeController.OnDisplayChangingListener mRotationController = (display, fromRotation, toRotation, wct) -> { if (mDisplayAreaOrganizer != null) { @@ -107,6 +109,37 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { public OneHandedManagerImpl(Context context, CommandQueue commandQueue, DisplayController displayController, + NavigationModeController navigationModeController, + SysUiState sysUiState) { + mCommandQueue = commandQueue; + mDisplayController = displayController; + mDisplayController.addDisplayChangingController(mRotationController); + mSysUiFlagContainer = sysUiState; + mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) / 100.0f; + + mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( + context.getContentResolver()); + mIsSwipeToNotificationEnabled = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + context.getContentResolver()); + mTimeoutHandler = OneHandedTimeoutHandler.get(); + mTouchHandler = new OneHandedTouchHandler(); + mTutorialHandler = new OneHandedTutorialHandler(context); + mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(context, displayController, + new OneHandedAnimationController(context), mTutorialHandler); + mGestureHandler = new OneHandedGestureHandler( + context, displayController, navigationModeController); + updateOneHandedEnabled(); + setupGestures(); + } + + /** + * Constructor of OneHandedManager for testing + */ + // TODO(b/161980408): Should remove extra constructor. + @VisibleForTesting + OneHandedManagerImpl(Context context, + CommandQueue commandQueue, + DisplayController displayController, OneHandedDisplayAreaOrganizer displayAreaOrganizer, OneHandedTouchHandler touchHandler, OneHandedTutorialHandler tutorialHandler, @@ -117,8 +150,8 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { mDisplayController = displayController; mDisplayController.addDisplayChangingController(mRotationController); mSysUiFlagContainer = sysUiState; - mOffSetFraction = - context.getResources().getFraction(R.fraction.config_one_handed_offset, 1, 1); + mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) / 100.0f; + mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( context.getContentResolver()); mIsSwipeToNotificationEnabled = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java index 1b6ec04193f0..0598f32c16d5 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java @@ -22,19 +22,16 @@ import android.database.ContentObserver; import android.net.Uri; import android.provider.Settings; -import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.dagger.SysUISingleton; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import javax.inject.Inject; -import javax.inject.Singleton; - /** * APIs for querying or updating one handed settings . */ -@Singleton +@SysUISingleton public final class OneHandedSettingsUtil { private static final String TAG = "OneHandedSettingsUtil"; @@ -65,11 +62,6 @@ public final class OneHandedSettingsUtil { */ public static final int ONE_HANDED_TIMEOUT_LONG_IN_SECONDS = 12; - @VisibleForTesting - @Inject - OneHandedSettingsUtil() { - } - /** * Register one handed preference settings observer * @@ -150,4 +142,6 @@ public final class OneHandedSettingsUtil { pw.print(innerPrefix + "tapsAppToExit="); pw.println(getSettingsTapsAppToExit(resolver)); } + + private OneHandedSettingsUtil() {} } diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSurfaceTransactionHelper.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSurfaceTransactionHelper.java index 5933eb333bf3..bc4a9b49205c 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSurfaceTransactionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSurfaceTransactionHelper.java @@ -23,8 +23,6 @@ import android.view.SurfaceControl; import com.android.systemui.R; -import javax.inject.Inject; - /** * Abstracts the common operations on {@link SurfaceControl.Transaction} for OneHanded transition. */ @@ -32,7 +30,6 @@ public class OneHandedSurfaceTransactionHelper { private final boolean mEnableCornerRadius; private final float mCornerRadius; - @Inject public OneHandedSurfaceTransactionHelper(Context context) { final Resources res = context.getResources(); mCornerRadius = res.getDimension(com.android.internal.R.dimen.rounded_corner_radius); diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTimeoutHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTimeoutHandler.java index 194b24f95458..6bed30425c55 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTimeoutHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTimeoutHandler.java @@ -33,12 +33,9 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -import javax.inject.Singleton; - /** * Timeout handler for stop one handed mode operations. */ -@Singleton public class OneHandedTimeoutHandler implements Dumpable { private static final String TAG = "OneHandedTimeoutHandler"; private static boolean sIsDragging = false; diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java index 1446e5a431c6..0a7eb1bdada0 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java @@ -35,15 +35,11 @@ import com.android.systemui.Dumpable; import java.io.FileDescriptor; import java.io.PrintWriter; -import javax.inject.Inject; -import javax.inject.Singleton; - /** * Manages all the touch handling for One Handed on the Phone, including user tap outside region * to exit, reset timer when user is in one-handed mode. * Refer {@link OneHandedGestureHandler} to see start and stop one handed gesture */ -@Singleton public class OneHandedTouchHandler implements OneHandedTransitionCallback, Dumpable { private static final String TAG = "OneHandedTouchHandler"; private final Rect mLastUpdatedBounds = new Rect(); @@ -61,7 +57,6 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback, Dumpa private boolean mIsOnStopTransitioning; private boolean mIsInOutsideRegion; - @Inject public OneHandedTouchHandler() { mTimeoutHandler = OneHandedTimeoutHandler.get(); updateIsEnabled(); diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java index b28730d004f6..8ef9b092bc00 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java @@ -22,6 +22,7 @@ import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; import android.os.Handler; +import android.os.SystemProperties; import android.provider.Settings; import android.view.Gravity; import android.view.LayoutInflater; @@ -38,18 +39,16 @@ import com.android.systemui.R; import java.io.FileDescriptor; import java.io.PrintWriter; -import javax.inject.Inject; -import javax.inject.Singleton; - /** * Manages the user tutorial handling for One Handed operations, including animations synchronized * with one-handed translation. * Refer {@link OneHandedGestureHandler} and {@link OneHandedTouchHandler} to see start and stop * one handed gesture */ -@Singleton public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Dumpable { private static final String TAG = "OneHandedTutorialHandler"; + private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE = + "persist.debug.one_handed_offset_percentage"; private static final int MAX_TUTORIAL_SHOW_COUNT = 2; private final Rect mLastUpdatedBounds = new Rect(); private final WindowManager mWindowManager; @@ -73,7 +72,6 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Du } }; - @Inject public OneHandedTutorialHandler(Context context) { context.getDisplay().getRealSize(mDisplaySize); mContentResolver = context.getContentResolver(); @@ -81,8 +79,8 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Du mWindowManager = context.getSystemService(WindowManager.class); mTargetViewContainer = new FrameLayout(context); mTargetViewContainer.setClipChildren(false); - mTutorialAreaHeight = Math.round(mDisplaySize.y * context.getResources().getFraction( - R.fraction.config_one_handed_offset, 1, 1)); + mTutorialAreaHeight = Math.round(mDisplaySize.y + * (SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) / 100.0f)); mTutorialView = LayoutInflater.from(context).inflate(R.xml.one_handed_tutorial, null); mTargetViewContainer.addView(mTutorialView); mCanShowTutorial = (Settings.Secure.getInt(mContentResolver, diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java index 0903c0e5c512..cebcd4ceabe9 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java @@ -40,6 +40,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.statusbar.CommandQueue; @@ -47,12 +48,11 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Singleton; /** * A service that controls UI of the one handed mode function. */ -@Singleton +@SysUISingleton public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dumpable { private static final String TAG = "OneHandedUI"; private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY = @@ -63,7 +63,6 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum private final CommandQueue mCommandQueue; private final Handler mMainHandler = new Handler(Looper.getMainLooper()); private final IOverlayManager mOverlayManager; - private final OneHandedSettingsUtil mSettingUtil; private final OneHandedTimeoutHandler mTimeoutHandler; private final ScreenLifecycle mScreenLifecycle; @@ -154,7 +153,6 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum public OneHandedUI(Context context, CommandQueue commandQueue, OneHandedManagerImpl oneHandedManager, - OneHandedSettingsUtil settingsUtil, ScreenLifecycle screenLifecycle) { super(context); @@ -163,7 +161,6 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum mCommandQueue = null; mOneHandedManager = null; mOverlayManager = null; - mSettingUtil = null; mTimeoutHandler = null; mScreenLifecycle = null; return; @@ -171,7 +168,6 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum mCommandQueue = commandQueue; mOneHandedManager = oneHandedManager; - mSettingUtil = settingsUtil; mTimeoutHandler = OneHandedTimeoutHandler.get(); mScreenLifecycle = screenLifecycle; mOverlayManager = IOverlayManager.Stub.asInterface( @@ -252,26 +248,26 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum } private void setupSettingObservers() { - mSettingUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED, + OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED, mContext.getContentResolver(), mEnabledObserver); - mSettingUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_TIMEOUT, + OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_TIMEOUT, mContext.getContentResolver(), mTimeoutObserver); - mSettingUtil.registerSettingsKeyObserver(Settings.Secure.TAPS_APP_TO_EXIT, + OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.TAPS_APP_TO_EXIT, mContext.getContentResolver(), mTaskChangeExitObserver); - mSettingUtil.registerSettingsKeyObserver( + OneHandedSettingsUtil.registerSettingsKeyObserver( Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, mContext.getContentResolver(), mSwipeToNotificationEnabledObserver); } private void updateSettings() { - mOneHandedManager.setOneHandedEnabled( - mSettingUtil.getSettingsOneHandedModeEnabled(mContext.getContentResolver())); - mTimeoutHandler.setTimeout( - mSettingUtil.getSettingsOneHandedModeTimeout(mContext.getContentResolver())); - mOneHandedManager.setTaskChangeToExit( - mSettingUtil.getSettingsTapsAppToExit(mContext.getContentResolver())); - mOneHandedManager.setSwipeToNotificationEnabled( - mSettingUtil.getSettingsSwipeToNotificationEnabled(mContext.getContentResolver())); + mOneHandedManager.setOneHandedEnabled(OneHandedSettingsUtil + .getSettingsOneHandedModeEnabled(mContext.getContentResolver())); + mTimeoutHandler.setTimeout(OneHandedSettingsUtil + .getSettingsOneHandedModeTimeout(mContext.getContentResolver())); + mOneHandedManager.setTaskChangeToExit(OneHandedSettingsUtil + .getSettingsTapsAppToExit(mContext.getContentResolver())); + mOneHandedManager.setSwipeToNotificationEnabled(OneHandedSettingsUtil + .getSettingsSwipeToNotificationEnabled(mContext.getContentResolver())); } @Override @@ -326,9 +322,7 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum mTimeoutHandler.dump(fd, pw, args); } - if (mSettingUtil != null) { - mSettingUtil.dump(pw, innerPrefix, mContext.getContentResolver()); - } + OneHandedSettingsUtil.dump(pw, innerPrefix, mContext.getContentResolver()); if (mOverlayManager != null) { OverlayInfo info = null; diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java index df3aeadaacd6..d6aa61b0c767 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java @@ -39,19 +39,19 @@ import android.view.DisplayInfo; import android.view.Gravity; import android.window.WindowContainerTransaction; +import com.android.systemui.dagger.SysUISingleton; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Singleton; /** * Handles bounds calculation for PIP on Phone and other form factors, it keeps tracking variant * state changes originated from Window Manager and is the source of truth for PiP window bounds. */ -@Singleton +@SysUISingleton public class PipBoundsHandler { private static final String TAG = PipBoundsHandler.class.getSimpleName(); diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java index 2c7ec48e4ae7..e88451ca00b5 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java @@ -23,16 +23,16 @@ import android.graphics.Rect; import android.graphics.RectF; import android.view.SurfaceControl; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.wm.shell.R; import javax.inject.Inject; -import javax.inject.Singleton; /** * Abstracts the common operations on {@link SurfaceControl.Transaction} for PiP transition. */ -@Singleton +@SysUISingleton public class PipSurfaceTransactionHelper implements ConfigurationController.ConfigurationListener { private final Context mContext; diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 025341cf4fba..0e60c83b8392 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -56,6 +56,7 @@ import android.window.WindowContainerTransactionCallback; import android.window.WindowOrganizer; import com.android.internal.os.SomeArgs; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.pip.phone.PipUpdateThread; import com.android.systemui.stackdivider.Divider; import com.android.wm.shell.R; @@ -70,7 +71,6 @@ import java.util.Objects; import java.util.function.Consumer; import javax.inject.Inject; -import javax.inject.Singleton; /** * Manages PiP tasks such as resize and offset. @@ -83,7 +83,7 @@ import javax.inject.Singleton; * This class is also responsible for general resize/offset PiP operations within SysUI component, * see also {@link com.android.systemui.pip.phone.PipMotionHelper}. */ -@Singleton +@SysUISingleton public class PipTaskOrganizer extends TaskOrganizer implements DisplayController.OnDisplaysChangedListener { private static final String TAG = PipTaskOrganizer.class.getSimpleName(); diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java index 4fb675e31f0e..2cd1e202125d 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java @@ -25,6 +25,7 @@ import android.os.UserHandle; import android.os.UserManager; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.statusbar.CommandQueue; @@ -32,12 +33,11 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controls the picture-in-picture window. */ -@Singleton +@SysUISingleton public class PipUI extends SystemUI implements CommandQueue.Callbacks { private final CommandQueue mCommandQueue; diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java index 5e2cd9c111b2..7ce2028b5f1b 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java @@ -20,15 +20,15 @@ import android.app.TaskInfo; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; +import com.android.systemui.dagger.SysUISingleton; import javax.inject.Inject; -import javax.inject.Singleton; /** * Helper class that ends PiP log to UiEvent, see also go/uievent */ -@Singleton +@SysUISingleton public class PipUiEventLogger { private final UiEventLogger mUiEventLogger; diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index 9dfa864e2ee7..facb3966f78c 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -42,6 +42,7 @@ import android.window.WindowContainerTransaction; import com.android.systemui.Dependency; import com.android.systemui.UiOffloadThread; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.model.SysUiState; import com.android.systemui.pip.BasePipManager; import com.android.systemui.pip.PipBoundsHandler; @@ -64,12 +65,11 @@ import com.android.wm.shell.common.DisplayController; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Singleton; /** * Manages the picture-in-picture (PIP) UI and states for Phones. */ -@Singleton +@SysUISingleton public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback { private static final String TAG = "PipManager"; @@ -233,7 +233,10 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio @Override public void onAspectRatioChanged(float aspectRatio) { - mHandler.post(() -> mPipBoundsHandler.onAspectRatioChanged(aspectRatio)); + mHandler.post(() -> { + mPipBoundsHandler.onAspectRatioChanged(aspectRatio); + mTouchHandler.onAspectRatioChanged(); + }); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java index 9c42f8bff378..2800bb938149 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java @@ -364,6 +364,10 @@ public class PipResizeGestureHandler { mUserResizeBounds.set(bounds); } + void invalidateUserResizeBounds() { + mUserResizeBounds.setEmpty(); + } + Rect getUserResizeBounds() { return mUserResizeBounds; } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index b20ea4e5c836..ecd315b336f2 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -426,8 +426,21 @@ public class PipTouchHandler { } } + /** + * Responds to IPinnedStackListener on resetting aspect ratio for the pinned window. + */ + public void onAspectRatioChanged() { + mPipResizeGestureHandler.invalidateUserResizeBounds(); + } + public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect curBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation) { + // Set the user resized bounds equal to the new normal bounds in case they were + // invalidated (e.g. by an aspect ratio change). + if (mPipResizeGestureHandler.getUserResizeBounds().isEmpty()) { + mPipResizeGestureHandler.setUserResizeBounds(normalBounds); + } + final int bottomOffset = mIsImeShowing ? mImeHeight : 0; final boolean fromDisplayRotationChanged = (mDisplayRotation != displayRotation); if (fromDisplayRotationChanged) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 01670285c0f4..74dc003bd8b6 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -49,6 +49,7 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.UiOffloadThread; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.pip.BasePipManager; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSurfaceTransactionHelper; @@ -63,12 +64,11 @@ import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * Manages the picture-in-picture (PIP) UI and states. */ -@Singleton +@SysUISingleton public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback { private static final String TAG = "PipManager"; static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -186,20 +186,23 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio private class PipManagerPinnedStackListener extends PinnedStackListener { @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - if (mState == STATE_PIP) { - if (mImeVisible != imeVisible) { - if (imeVisible) { - // Save the IME height adjustment, and offset to not occlude the IME - mPipBounds.offset(0, -imeHeight); - mImeHeightAdjustment = imeHeight; - } else { - // Apply the inverse adjustment when the IME is hidden - mPipBounds.offset(0, mImeHeightAdjustment); + mHandler.post(() -> { + mPipBoundsHandler.onImeVisibilityChanged(imeVisible, imeHeight); + if (mState == STATE_PIP) { + if (mImeVisible != imeVisible) { + if (imeVisible) { + // Save the IME height adjustment, and offset to not occlude the IME + mPipBounds.offset(0, -imeHeight); + mImeHeightAdjustment = imeHeight; + } else { + // Apply the inverse adjustment when the IME is hidden + mPipBounds.offset(0, mImeHeightAdjustment); + } + mImeVisible = imeVisible; + resizePinnedStack(STATE_PIP); } - mImeVisible = imeVisible; - resizePinnedStack(STATE_PIP); } - } + }); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java index 69495319e060..ad1e21d7cc45 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java @@ -17,15 +17,15 @@ package com.android.systemui.plugins; import android.util.ArrayMap; import com.android.systemui.Dependency; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.PluginDependency.DependencyProvider; import com.android.systemui.shared.plugins.PluginManager; import javax.inject.Inject; -import javax.inject.Singleton; /** */ -@Singleton +@SysUISingleton public class PluginDependencyProvider extends DependencyProvider { private final ArrayMap<Class<?>, Object> mDependencies = new ArrayMap<>(); diff --git a/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java index 7d54c211242f..90da8912ad75 100644 --- a/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java @@ -2,11 +2,11 @@ package com.android.systemui.power; import com.android.settingslib.fuelgauge.Estimate; import com.android.settingslib.fuelgauge.EstimateKt; +import com.android.systemui.dagger.SysUISingleton; import javax.inject.Inject; -import javax.inject.Singleton; -@Singleton +@SysUISingleton public class EnhancedEstimatesImpl implements EnhancedEstimates { @Inject diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java index 6abbbbeaa397..a27e9ac61848 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java @@ -59,6 +59,7 @@ import com.android.settingslib.utils.PowerUtil; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.util.NotificationChannels; @@ -70,11 +71,10 @@ import java.util.Locale; import java.util.Objects; import javax.inject.Inject; -import javax.inject.Singleton; /** */ -@Singleton +@SysUISingleton public class PowerNotificationWarnings implements PowerUI.WarningsUI { private static final String TAG = PowerUI.TAG + ".Notification"; diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java index 66804bef8f53..a888305cc83d 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -46,6 +46,7 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.StatusBar; @@ -56,11 +57,10 @@ import java.util.Arrays; import java.util.concurrent.Future; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; -@Singleton +@SysUISingleton public class PowerUI extends SystemUI implements CommandQueue.Callbacks { static final String TAG = "PowerUI"; diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index affc5ee1fdf0..255ba1b61db1 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -31,6 +31,7 @@ import com.android.systemui.Dumpable import com.android.systemui.appops.AppOpItem import com.android.systemui.appops.AppOpsController import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager @@ -41,9 +42,8 @@ import java.io.PrintWriter import java.lang.ref.WeakReference import java.util.concurrent.Executor import javax.inject.Inject -import javax.inject.Singleton -@Singleton +@SysUISingleton class PrivacyItemController @Inject constructor( private val appOpsController: AppOpsController, @Main uiExecutor: DelayableExecutor, diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 3b16a4ec1c38..9a63a56b2c8e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -37,6 +37,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; @@ -69,10 +70,9 @@ import java.util.function.Predicate; import javax.inject.Inject; import javax.inject.Provider; -import javax.inject.Singleton; /** Platform implementation of the quick settings tile host **/ -@Singleton +@SysUISingleton public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, Dumpable { private static final String TAG = "QSTileHost"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index 69a6fe1075c2..8e33496e73af 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -20,6 +20,7 @@ import android.util.Log; import android.view.ContextThemeWrapper; import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.qs.QSIconView; import com.android.systemui.plugins.qs.QSTile; @@ -49,11 +50,10 @@ import com.android.systemui.util.leak.GarbageMonitor; import javax.inject.Inject; import javax.inject.Provider; -import javax.inject.Singleton; import dagger.Lazy; -@Singleton +@SysUISingleton public class QSFactoryImpl implements QSFactory { private static final String TAG = "QSFactory"; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index d2aaaede3f4b..255513a31c75 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -14,6 +14,7 @@ package com.android.systemui.qs.tileimpl; +import static androidx.lifecycle.Lifecycle.State.CREATED; import static androidx.lifecycle.Lifecycle.State.DESTROYED; import static androidx.lifecycle.Lifecycle.State.RESUMED; import static androidx.lifecycle.Lifecycle.State.STARTED; @@ -173,6 +174,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy mState = newTileState(); mTmpState = newTileState(); + mUiHandler.post(() -> mLifecycle.setCurrentState(CREATED)); } protected final void resetStates() { @@ -453,6 +455,9 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy if (DEBUG) Log.d(TAG, "handleSetListening true"); handleSetListening(listening); mUiHandler.post(() -> { + // This tile has been destroyed, the state should not change anymore and we + // should not refresh it anymore. + if (mLifecycle.getCurrentState().equals(DESTROYED)) return; mLifecycle.setCurrentState(RESUMED); refreshState(); // Ensure we get at least one refresh after listening. }); @@ -461,7 +466,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy if (mListeners.remove(listener) && mListeners.size() == 0) { if (DEBUG) Log.d(TAG, "handleSetListening false"); handleSetListening(listening); - mUiHandler.post(() -> mLifecycle.setCurrentState(STARTED)); + mUiHandler.post(() -> { + // This tile has been destroyed, the state should not change anymore. + if (mLifecycle.getCurrentState().equals(DESTROYED)) return; + mLifecycle.setCurrentState(STARTED); + }); } } updateIsFullQs(); @@ -488,11 +497,14 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy mQSLogger.logTileDestroyed(mTileSpec, "Handle destroy"); if (mListeners.size() != 0) { handleSetListening(false); + mListeners.clear(); } mCallbacks.clear(); mHandler.removeCallbacksAndMessages(null); // This will force it to be removed from all controllers that may have it registered. - mLifecycle.setCurrentState(DESTROYED); + mUiHandler.post(() -> { + mLifecycle.setCurrentState(DESTROYED); + }); } protected void checkIfRestrictionEnforcedByAdminOnly(State state, String userRestriction) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java index f89185e3efa9..0347867b61fc 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java @@ -35,6 +35,7 @@ import android.widget.Toast; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.stackdivider.Divider; @@ -43,14 +44,13 @@ import com.android.systemui.statusbar.phone.StatusBar; import java.util.Optional; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; /** * An implementation of the Recents interface which proxies to the OverviewProxyService. */ -@Singleton +@SysUISingleton public class OverviewProxyRecentsImpl implements RecentsImplementation { private final static String TAG = "OverviewProxyRecentsImpl"; diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index d03082e6b442..e931a6bf179a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -61,12 +61,19 @@ import android.view.MotionEvent; import android.view.Surface; import android.view.accessibility.AccessibilityManager; +import androidx.annotation.NonNull; + import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.util.ScreenshotHelper; import com.android.systemui.Dumpable; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.NavigationBar; +import com.android.systemui.navigationbar.NavigationBarController; +import com.android.systemui.navigationbar.NavigationBarView; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.onehanded.OneHandedUI; import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipUI; @@ -81,11 +88,7 @@ import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.NavigationBarController; -import com.android.systemui.statusbar.phone.NavigationBarFragment; -import com.android.systemui.statusbar.phone.NavigationBarView; -import com.android.systemui.statusbar.phone.NavigationModeController; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarWindowCallback; import com.android.systemui.statusbar.policy.CallbackController; @@ -97,14 +100,13 @@ import java.util.List; import java.util.Optional; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; /** * Class to send information from overview to launcher with a binder. */ -@Singleton +@SysUISingleton public class OverviewProxyService extends CurrentUserTracker implements CallbackController<OverviewProxyListener>, NavigationModeController.ModeChangedListener, Dumpable { @@ -124,7 +126,7 @@ public class OverviewProxyService extends CurrentUserTracker implements private final Optional<Divider> mDividerOptional; private SysUiState mSysUiState; private final Handler mHandler; - private final NavigationBarController mNavBarController; + private final Lazy<NavigationBarController> mNavBarControllerLazy; private final NotificationShadeWindowController mStatusBarWinController; private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser; private final ComponentName mRecentsComponentName; @@ -598,7 +600,7 @@ public class OverviewProxyService extends CurrentUserTracker implements @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @Inject public OverviewProxyService(Context context, CommandQueue commandQueue, - NavigationBarController navBarController, NavigationModeController navModeController, + Lazy<NavigationBarController> navBarControllerLazy, NavigationModeController navModeController, NotificationShadeWindowController statusBarWinController, SysUiState sysUiState, PipUI pipUI, Optional<Divider> dividerOptional, Optional<Lazy<StatusBar>> statusBarOptionalLazy, OneHandedUI oneHandedUI, @@ -608,7 +610,7 @@ public class OverviewProxyService extends CurrentUserTracker implements mPipUI = pipUI; mStatusBarOptionalLazy = statusBarOptionalLazy; mHandler = new Handler(); - mNavBarController = navBarController; + mNavBarControllerLazy = navBarControllerLazy; mStatusBarWinController = statusBarWinController; mConnectionBackoffAttempts = 0; mDividerOptional = dividerOptional; @@ -677,10 +679,10 @@ public class OverviewProxyService extends CurrentUserTracker implements } private void updateSystemUiStateFlags() { - final NavigationBarFragment navBarFragment = - mNavBarController.getDefaultNavigationBarFragment(); + final NavigationBar navBarFragment = + mNavBarControllerLazy.get().getDefaultNavigationBar(); final NavigationBarView navBarView = - mNavBarController.getNavigationBarView(mContext.getDisplayId()); + mNavBarControllerLazy.get().getNavigationBarView(mContext.getDisplayId()); if (SysUiState.DEBUG) { Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment + " navBarView=" + navBarView); @@ -808,7 +810,7 @@ public class OverviewProxyService extends CurrentUserTracker implements } @Override - public void addCallback(OverviewProxyListener listener) { + public void addCallback(@NonNull OverviewProxyListener listener) { if (!mConnectionCallbacks.contains(listener)) { mConnectionCallbacks.add(listener); } @@ -817,7 +819,7 @@ public class OverviewProxyService extends CurrentUserTracker implements } @Override - public void removeCallback(OverviewProxyListener listener) { + public void removeCallback(@NonNull OverviewProxyListener listener) { mConnectionCallbacks.remove(listener); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java index 3b3d9dde3b7e..9c5a3de4523a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java @@ -466,23 +466,22 @@ public class RecentsOnboarding { } public void dump(PrintWriter pw) { - pw.println("RecentsOnboarding {"); - pw.println(" mTaskListenerRegistered: " + mTaskListenerRegistered); - pw.println(" mOverviewProxyListenerRegistered: " + mOverviewProxyListenerRegistered); - pw.println(" mLayoutAttachedToWindow: " + mLayoutAttachedToWindow); - pw.println(" mHasDismissedSwipeUpTip: " + mHasDismissedSwipeUpTip); - pw.println(" mHasDismissedQuickScrubTip: " + mHasDismissedQuickScrubTip); - pw.println(" mNumAppsLaunchedSinceSwipeUpTipDismiss: " + pw.println("RecentsOnboarding"); + pw.println(" mTaskListenerRegistered: " + mTaskListenerRegistered); + pw.println(" mOverviewProxyListenerRegistered: " + mOverviewProxyListenerRegistered); + pw.println(" mLayoutAttachedToWindow: " + mLayoutAttachedToWindow); + pw.println(" mHasDismissedSwipeUpTip: " + mHasDismissedSwipeUpTip); + pw.println(" mHasDismissedQuickScrubTip: " + mHasDismissedQuickScrubTip); + pw.println(" mNumAppsLaunchedSinceSwipeUpTipDismiss: " + mNumAppsLaunchedSinceSwipeUpTipDismiss); - pw.println(" hasSeenSwipeUpOnboarding: " + hasSeenSwipeUpOnboarding()); - pw.println(" hasSeenQuickScrubOnboarding: " + hasSeenQuickScrubOnboarding()); - pw.println(" getDismissedSwipeUpOnboardingCount: " + pw.println(" hasSeenSwipeUpOnboarding: " + hasSeenSwipeUpOnboarding()); + pw.println(" hasSeenQuickScrubOnboarding: " + hasSeenQuickScrubOnboarding()); + pw.println(" getDismissedSwipeUpOnboardingCount: " + getDismissedSwipeUpOnboardingCount()); - pw.println(" hasDismissedQuickScrubOnboardingOnce: " + pw.println(" hasDismissedQuickScrubOnboardingOnce: " + hasDismissedQuickScrubOnboardingOnce()); - pw.println(" getOpenedOverviewCount: " + getOpenedOverviewCount()); - pw.println(" getOpenedOverviewFromHomeCount: " + getOpenedOverviewFromHomeCount()); - pw.println(" }"); + pw.println(" getOpenedOverviewCount: " + getOpenedOverviewCount()); + pw.println(" getOpenedOverviewFromHomeCount: " + getOpenedOverviewFromHomeCount()); } private WindowManager.LayoutParams getWindowLayoutParams(int gravity, int x) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java index 387490311644..6afc75624a9f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java +++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java @@ -51,8 +51,8 @@ import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.WindowManagerWrapper; -import com.android.systemui.statusbar.phone.NavigationBarView; -import com.android.systemui.statusbar.phone.NavigationModeController; +import com.android.systemui.navigationbar.NavigationBarView; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.leak.RotationUtils; diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java index 82ac1f6f6a33..10a44ddb17f2 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java @@ -26,19 +26,21 @@ import android.os.CountDownTimer; import android.os.UserHandle; import android.util.Log; +import androidx.annotation.NonNull; + import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.policy.CallbackController; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Singleton; /** * Helper class to initiate a screen recording */ -@Singleton +@SysUISingleton public class RecordingController implements CallbackController<RecordingController.RecordingStateChangeCallback> { private static final String TAG = "RecordingController"; @@ -191,12 +193,12 @@ public class RecordingController } @Override - public void addCallback(RecordingStateChangeCallback listener) { + public void addCallback(@NonNull RecordingStateChangeCallback listener) { mListeners.add(listener); } @Override - public void removeCallback(RecordingStateChangeCallback listener) { + public void removeCallback(@NonNull RecordingStateChangeCallback listener) { mListeners.remove(listener); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 6747281ac437..e24fbc6cca9d 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -81,6 +81,7 @@ import android.widget.Toast; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.system.QuickStepContract; @@ -89,12 +90,11 @@ import java.util.List; import java.util.function.Consumer; import javax.inject.Inject; -import javax.inject.Singleton; /** * Class for handling device screen shots */ -@Singleton +@SysUISingleton public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInsetsListener { /** diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java index b5209bbbdd21..a48870240384 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java @@ -65,10 +65,10 @@ public class ScreenshotActionChip extends FrameLayout { } void setIcon(Icon icon, boolean tint) { - if (tint) { - icon.setTint(mIconColor); - } mIcon.setImageIcon(icon); + if (!tint) { + mIcon.setImageTintList(null); + } } void setText(CharSequence text) { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java index 633cdd6ca5ca..468602a5369e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java @@ -31,6 +31,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.SystemUIFactory; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.shared.system.ActivityManagerWrapper; import java.util.Collections; @@ -40,12 +41,11 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import javax.inject.Inject; -import javax.inject.Singleton; /** * Collects the static functions for retrieving and acting on smart actions. */ -@Singleton +@SysUISingleton public class ScreenshotSmartActions { private static final String TAG = "ScreenshotSmartActions"; diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java index eb5bd5c01a78..b1ed77275187 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java +++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java @@ -19,11 +19,10 @@ package com.android.systemui.settings.dagger; import android.content.Context; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.settings.CurrentUserContentResolverProvider; import com.android.systemui.settings.CurrentUserContextTracker; -import javax.inject.Singleton; - import dagger.Binds; import dagger.Module; import dagger.Provides; @@ -37,7 +36,7 @@ public abstract class SettingsModule { /** * Provides and initializes a CurrentUserContextTracker */ - @Singleton + @SysUISingleton @Provides static CurrentUserContextTracker provideCurrentUserContextTracker( Context context, @@ -49,7 +48,7 @@ public abstract class SettingsModule { } @Binds - @Singleton + @SysUISingleton abstract CurrentUserContentResolverProvider bindCurrentUserContentResolverTracker( CurrentUserContextTracker tracker); } diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java index f7f12239c6db..ee3303bf33ef 100644 --- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java @@ -28,17 +28,17 @@ import android.view.WindowManagerGlobal; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.recents.Recents; import com.android.systemui.stackdivider.Divider; import com.android.systemui.stackdivider.DividerView; import javax.inject.Inject; -import javax.inject.Singleton; /** * Dispatches shortcut to System UI components */ -@Singleton +@SysUISingleton public class ShortcutKeyDispatcher extends SystemUI implements ShortcutKeyServiceProxy.Callbacks { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index d40b666860e9..e9c880ebbcc9 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -23,6 +23,7 @@ import android.content.Context; import android.window.WindowContainerToken; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -31,12 +32,10 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.function.Consumer; -import javax.inject.Singleton; - /** * Controls the docked stack divider. */ -@Singleton +@SysUISingleton public class Divider extends SystemUI { private final KeyguardStateController mKeyguardStateController; private final DividerController mDividerController; diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java index db0aef8a0611..9c2139772e7d 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java @@ -19,6 +19,7 @@ package com.android.systemui.stackdivider; import android.content.Context; import android.os.Handler; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.wm.shell.common.DisplayController; @@ -26,8 +27,6 @@ import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TransactionPool; -import javax.inject.Singleton; - import dagger.Module; import dagger.Provides; @@ -36,7 +35,7 @@ import dagger.Provides; */ @Module public class DividerModule { - @Singleton + @SysUISingleton @Provides static Divider provideDivider(Context context, DisplayController displayController, SystemWindows systemWindows, DisplayImeController imeController, @Main Handler handler, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt index c70d3847bd6f..f758db8a6ba4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt @@ -26,14 +26,14 @@ import androidx.annotation.VisibleForTesting import com.android.internal.util.IndentingPrintWriter import com.android.systemui.Dumpable import com.android.systemui.R +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import java.io.FileDescriptor import java.io.PrintWriter import javax.inject.Inject -import javax.inject.Singleton -@Singleton +@SysUISingleton open class BlurUtils @Inject constructor( @Main private val resources: Resources, dumpManager: DumpManager diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 1638dd9664a4..4673ec73c25a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -49,6 +49,8 @@ import android.util.SparseArray; import android.view.InsetsState.InternalInsetsType; import android.view.WindowInsetsController.Appearance; +import androidx.annotation.NonNull; + import com.android.internal.os.SomeArgs; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.StatusBarIcon; @@ -391,7 +393,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< && !ONLY_CORE_APPS; } - public void addCallback(Callbacks callbacks) { + @Override + public void addCallback(@NonNull Callbacks callbacks) { mCallbacks.add(callbacks); // TODO(b/117478341): find a better way to pass disable flags by display. for (int i = 0; i < mDisplayDisabled.size(); i++) { @@ -402,7 +405,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } } - public void removeCallback(Callbacks callbacks) { + @Override + public void removeCallback(@NonNull Callbacks callbacks) { mCallbacks.remove(callbacks); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index 6839921e90a7..3811ca929f6e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -20,13 +20,13 @@ import android.annotation.NonNull; import android.provider.DeviceConfig; import android.util.ArrayMap; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import java.util.Map; import java.util.concurrent.Executor; import javax.inject.Inject; -import javax.inject.Singleton; /** * Class to manage simple DeviceConfig-based feature flags. @@ -43,7 +43,7 @@ import javax.inject.Singleton; * $ adb shell am restart com.android.systemui * } */ -@Singleton +@SysUISingleton public class FeatureFlags { private final Map<String, Boolean> mCachedDeviceConfigFlags = new ArrayMap<>(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 7e1dc6634cec..a59ff38896dc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -55,6 +55,7 @@ import com.android.settingslib.fuelgauge.BatteryStatus; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; @@ -71,12 +72,11 @@ import java.text.NumberFormat; import java.util.IllegalFormatConversionException; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controls the indications and error messages shown on the Keyguard */ -@Singleton +@SysUISingleton public class KeyguardIndicationController implements StateListener, KeyguardStateController.Callback { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt index 326757e9a4c1..750272d65659 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt @@ -28,17 +28,16 @@ import android.renderscript.ScriptIntrinsicBlur import android.util.Log import android.util.MathUtils import com.android.internal.graphics.ColorUtils +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.MediaNotificationProcessor - import javax.inject.Inject -import javax.inject.Singleton private const val TAG = "MediaArtworkProcessor" private const val COLOR_ALPHA = (255 * 0.7f).toInt() private const val BLUR_RADIUS = 25f private const val DOWNSAMPLE = 6 -@Singleton +@SysUISingleton class MediaArtworkProcessor @Inject constructor() { private val mTmpSize = Point() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java deleted file mode 100644 index 2638d28733e8..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.systemui.statusbar; - -import static android.view.Display.DEFAULT_DISPLAY; - -import android.content.Context; -import android.hardware.display.DisplayManager; -import android.os.Handler; -import android.os.RemoteException; -import android.util.Log; -import android.util.SparseArray; -import android.view.Display; -import android.view.IWindowManager; -import android.view.View; -import android.view.WindowManagerGlobal; - -import androidx.annotation.Nullable; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.statusbar.RegisterStatusBarResult; -import com.android.systemui.Dependency; -import com.android.systemui.assist.AssistHandleViewController; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.fragments.FragmentHostManager; -import com.android.systemui.plugins.DarkIconDispatcher; -import com.android.systemui.statusbar.CommandQueue.Callbacks; -import com.android.systemui.statusbar.phone.AutoHideController; -import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode; -import com.android.systemui.statusbar.phone.LightBarController; -import com.android.systemui.statusbar.phone.NavigationBarFragment; -import com.android.systemui.statusbar.phone.NavigationBarView; -import com.android.systemui.statusbar.phone.NavigationModeController; -import com.android.systemui.statusbar.policy.BatteryController; - -import javax.inject.Inject; -import javax.inject.Singleton; - - -/** A controller to handle navigation bars. */ -@Singleton -public class NavigationBarController implements Callbacks { - - private static final String TAG = NavigationBarController.class.getSimpleName(); - - private final Context mContext; - private final Handler mHandler; - private final DisplayManager mDisplayManager; - - /** A displayId - nav bar maps. */ - @VisibleForTesting - SparseArray<NavigationBarFragment> mNavigationBars = new SparseArray<>(); - - @Inject - public NavigationBarController(Context context, @Main Handler handler, - CommandQueue commandQueue) { - mContext = context; - mHandler = handler; - mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); - commandQueue.addCallback(this); - } - - @Override - public void onDisplayRemoved(int displayId) { - removeNavigationBar(displayId); - } - - @Override - public void onDisplayReady(int displayId) { - Display display = mDisplayManager.getDisplay(displayId); - createNavigationBar(display, null); - } - - // TODO(b/117478341): I use {@code includeDefaultDisplay} to make this method compatible to - // CarStatusBar because they have their own nav bar. Think about a better way for it. - /** - * Creates navigation bars when car/status bar initializes. - * - * @param includeDefaultDisplay {@code true} to create navigation bar on default display. - */ - public void createNavigationBars(final boolean includeDefaultDisplay, - RegisterStatusBarResult result) { - Display[] displays = mDisplayManager.getDisplays(); - for (Display display : displays) { - if (includeDefaultDisplay || display.getDisplayId() != DEFAULT_DISPLAY) { - createNavigationBar(display, result); - } - } - } - - /** - * Adds a navigation bar on default display or an external display if the display supports - * system decorations. - * - * @param display the display to add navigation bar on. - */ - @VisibleForTesting - void createNavigationBar(Display display, RegisterStatusBarResult result) { - if (display == null) { - return; - } - - final int displayId = display.getDisplayId(); - final boolean isOnDefaultDisplay = displayId == DEFAULT_DISPLAY; - final IWindowManager wms = WindowManagerGlobal.getWindowManagerService(); - - try { - if (!wms.hasNavigationBar(displayId)) { - return; - } - } catch (RemoteException e) { - // Cannot get wms, just return with warning message. - Log.w(TAG, "Cannot get WindowManager."); - return; - } - final Context context = isOnDefaultDisplay - ? mContext - : mContext.createDisplayContext(display); - NavigationBarFragment.create(context, (tag, fragment) -> { - NavigationBarFragment navBar = (NavigationBarFragment) fragment; - - // Unfortunately, we still need it because status bar needs LightBarController - // before notifications creation. We cannot directly use getLightBarController() - // from NavigationBarFragment directly. - LightBarController lightBarController = isOnDefaultDisplay - ? Dependency.get(LightBarController.class) - : new LightBarController(context, - Dependency.get(DarkIconDispatcher.class), - Dependency.get(BatteryController.class), - Dependency.get(NavigationModeController.class)); - navBar.setLightBarController(lightBarController); - - // TODO(b/118592525): to support multi-display, we start to add something which is - // per-display, while others may be global. I think it's time to add - // a new class maybe named DisplayDependency to solve per-display - // Dependency problem. - AutoHideController autoHideController = isOnDefaultDisplay - ? Dependency.get(AutoHideController.class) - : new AutoHideController(context, mHandler, - Dependency.get(IWindowManager.class)); - navBar.setAutoHideController(autoHideController); - navBar.restoreAppearanceAndTransientState(); - mNavigationBars.put(displayId, navBar); - - if (result != null) { - navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken, - result.mImeWindowVis, result.mImeBackDisposition, - result.mShowImeSwitcher); - } - }); - } - - private void removeNavigationBar(int displayId) { - NavigationBarFragment navBar = mNavigationBars.get(displayId); - if (navBar != null) { - navBar.setAutoHideController(/* autoHideController */ null); - View navigationWindow = navBar.getView().getRootView(); - WindowManagerGlobal.getInstance() - .removeView(navigationWindow, true /* immediate */); - // Also remove FragmentHostState here in case that onViewDetachedFromWindow has not yet - // invoked after display removal. - FragmentHostManager.removeAndDestroy(navigationWindow); - mNavigationBars.remove(displayId); - } - } - - /** @see NavigationBarFragment#checkNavBarModes() */ - public void checkNavBarModes(int displayId) { - NavigationBarFragment navBar = mNavigationBars.get(displayId); - if (navBar != null) { - navBar.checkNavBarModes(); - } - } - - /** @see NavigationBarFragment#finishBarAnimations() */ - public void finishBarAnimations(int displayId) { - NavigationBarFragment navBar = mNavigationBars.get(displayId); - if (navBar != null) { - navBar.finishBarAnimations(); - } - } - - /** @see NavigationBarFragment#touchAutoDim() */ - public void touchAutoDim(int displayId) { - NavigationBarFragment navBar = mNavigationBars.get(displayId); - if (navBar != null) { - navBar.touchAutoDim(); - } - } - - /** @see NavigationBarFragment#transitionTo(int, boolean) */ - public void transitionTo(int displayId, @TransitionMode int barMode, boolean animate) { - NavigationBarFragment navBar = mNavigationBars.get(displayId); - if (navBar != null) { - navBar.transitionTo(barMode, animate); - } - } - - /** @see NavigationBarFragment#disableAnimationsDuringHide(long) */ - public void disableAnimationsDuringHide(int displayId, long delay) { - NavigationBarFragment navBar = mNavigationBars.get(displayId); - if (navBar != null) { - navBar.disableAnimationsDuringHide(delay); - } - } - - /** @return {@link NavigationBarView} on the default display. */ - public @Nullable NavigationBarView getDefaultNavigationBarView() { - return getNavigationBarView(DEFAULT_DISPLAY); - } - - /** - * @param displayId the ID of display which Navigation bar is on - * @return {@link NavigationBarView} on the display with {@code displayId}. - * {@code null} if no navigation bar on that display. - */ - public @Nullable NavigationBarView getNavigationBarView(int displayId) { - NavigationBarFragment navBar = mNavigationBars.get(displayId); - return (navBar == null) ? null : (NavigationBarView) navBar.getView(); - } - - /** @return {@link NavigationBarFragment} on the default display. */ - @Nullable - public NavigationBarFragment getDefaultNavigationBarFragment() { - return mNavigationBars.get(DEFAULT_DISPLAY); - } - - /** @return {@link AssistHandleViewController} (only on the default display). */ - @Nullable - public AssistHandleViewController getAssistHandlerViewController() { - NavigationBarFragment navBar = getDefaultNavigationBarFragment(); - return navBar == null ? null : navBar.getAssistHandlerViewController(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt index 8248fc9f5844..abf81c5c4cb6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt @@ -4,11 +4,11 @@ import android.app.Notification import android.os.RemoteException import com.android.internal.statusbar.IStatusBarService import com.android.internal.statusbar.NotificationVisibility +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.util.Assert import java.util.concurrent.Executor import javax.inject.Inject -import javax.inject.Singleton /** * Class to shim calls to IStatusBarManager#onNotificationClick/#onNotificationActionClick that @@ -18,7 +18,7 @@ import javax.inject.Singleton * NOTE: this class eats exceptions from system server, as no current client of these APIs cares * about errors */ -@Singleton +@SysUISingleton public class NotificationClickNotifier @Inject constructor( val barService: IStatusBarService, @Main val mainExecutor: Executor diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInteractionTracker.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInteractionTracker.kt index 9dbec1037aa5..2ca1bebfcf9f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInteractionTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInteractionTracker.kt @@ -1,16 +1,16 @@ package com.android.systemui.statusbar +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.NotificationEntryManager import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import javax.inject.Inject -import javax.inject.Singleton /** * Class to track user interaction with notifications. It's a glorified map of key : bool that can * merge multiple "user interacted with notification" signals into a single place. */ -@Singleton +@SysUISingleton class NotificationInteractionTracker @Inject constructor( private val clicker: NotificationClickNotifier, private val entryManager: NotificationEntryManager diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 03424c4956ad..8d82270c9ca7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -48,6 +48,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; @@ -65,13 +66,12 @@ import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * Handles keeping track of the current user, profiles, and various things related to hiding * contents, redacting notifications, and the lockscreen. */ -@Singleton +@SysUISingleton public class NotificationLockscreenUserManagerImpl implements Dumpable, NotificationLockscreenUserManager, StateListener { private static final String TAG = "LockscreenUserManager"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 739d30c2a707..c01bdc4c2f28 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -60,7 +60,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.LockscreenWallpaper; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ScrimState; import com.android.systemui.statusbar.phone.StatusBar; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 6b023c07b1a6..95867957f648 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -161,7 +161,9 @@ public class NotificationRemoteInputManager implements Dumpable { ActivityManager.getService().resumeAppSwitches(); } catch (RemoteException e) { } - return mCallback.handleRemoteViewClick(view, pendingIntent, () -> { + Notification.Action action = getActionFromView(view, entry, pendingIntent); + return mCallback.handleRemoteViewClick(view, pendingIntent, + action == null ? false : action.isAuthenticationRequired(), () -> { Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view); options.second.setLaunchWindowingMode( WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); @@ -170,47 +172,56 @@ public class NotificationRemoteInputManager implements Dumpable { }); } - private void logActionClick( - View view, - NotificationEntry entry, - PendingIntent actionIntent) { + private @Nullable Notification.Action getActionFromView(View view, + NotificationEntry entry, PendingIntent actionIntent) { Integer actionIndex = (Integer) view.getTag(com.android.internal.R.id.notification_action_index_tag); if (actionIndex == null) { - // Custom action button, not logging. - return; + return null; } - ViewParent parent = view.getParent(); if (entry == null) { Log.w(TAG, "Couldn't determine notification for click."); - return; - } - StatusBarNotification statusBarNotification = entry.getSbn(); - String key = statusBarNotification.getKey(); - int buttonIndex = -1; - // If this is a default template, determine the index of the button. - if (view.getId() == com.android.internal.R.id.action0 && - parent != null && parent instanceof ViewGroup) { - ViewGroup actionGroup = (ViewGroup) parent; - buttonIndex = actionGroup.indexOfChild(view); + return null; } - final int count = mEntryManager.getActiveNotificationsCount(); - final int rank = mEntryManager - .getActiveNotificationUnfiltered(key).getRanking().getRank(); // Notification may be updated before this function is executed, and thus play safe // here and verify that the action object is still the one that where the click happens. + StatusBarNotification statusBarNotification = entry.getSbn(); Notification.Action[] actions = statusBarNotification.getNotification().actions; if (actions == null || actionIndex >= actions.length) { Log.w(TAG, "statusBarNotification.getNotification().actions is null or invalid"); - return; + return null ; } final Notification.Action action = statusBarNotification.getNotification().actions[actionIndex]; if (!Objects.equals(action.actionIntent, actionIntent)) { Log.w(TAG, "actionIntent does not match"); + return null; + } + return action; + } + + private void logActionClick( + View view, + NotificationEntry entry, + PendingIntent actionIntent) { + Notification.Action action = getActionFromView(view, entry, actionIntent); + if (action == null) { return; } + ViewParent parent = view.getParent(); + String key = entry.getSbn().getKey(); + int buttonIndex = -1; + // If this is a default template, determine the index of the button. + if (view.getId() == com.android.internal.R.id.action0 && + parent != null && parent instanceof ViewGroup) { + ViewGroup actionGroup = (ViewGroup) parent; + buttonIndex = actionGroup.indexOfChild(view); + } + final int count = mEntryManager.getActiveNotificationsCount(); + final int rank = mEntryManager + .getActiveNotificationUnfiltered(key).getRanking().getRank(); + NotificationVisibility.NotificationLocation location = NotificationLogger.getNotificationLocation( mEntryManager.getActiveNotificationUnfiltered(key)); @@ -813,11 +824,12 @@ public class NotificationRemoteInputManager implements Dumpable { * * @param view * @param pendingIntent + * @param appRequestedAuth * @param defaultHandler * @return true iff the click was handled */ boolean handleRemoteViewClick(View view, PendingIntent pendingIntent, - ClickHandler defaultHandler); + boolean appRequestedAuth, ClickHandler defaultHandler); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index 0445c9879ac5..c1196d65b702 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -32,27 +32,26 @@ import androidx.dynamicanimation.animation.SpringForce import com.android.internal.util.IndentingPrintWriter import com.android.systemui.Dumpable import com.android.systemui.Interpolators +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.notification.ActivityLaunchAnimator import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK import com.android.systemui.statusbar.phone.DozeParameters -import com.android.systemui.statusbar.phone.NotificationShadeWindowController import com.android.systemui.statusbar.phone.PanelExpansionListener import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.statusbar.policy.KeyguardStateController import java.io.FileDescriptor import java.io.PrintWriter import javax.inject.Inject -import javax.inject.Singleton import kotlin.math.max import kotlin.math.sign /** * Controller responsible for statusbar window blur. */ -@Singleton +@SysUISingleton class NotificationShadeDepthController @Inject constructor( private val statusBarStateController: StatusBarStateController, private val blurUtils: BlurUtils, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java new file mode 100644 index 000000000000..1fd0b03a77b7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.statusbar; + +import android.view.ViewGroup; + +import androidx.annotation.Nullable; + +import com.android.systemui.statusbar.phone.StatusBarWindowCallback; + +import java.util.function.Consumer; + +/** + * Interface to control the state of the notification shade window. Not all methods of this + * interface will be used by each implementation of {@link NotificationShadeWindowController}. + */ +public interface NotificationShadeWindowController extends RemoteInputController.Callback { + + /** + * Registers a {@link StatusBarWindowCallback} to receive notifications about status bar + * window state changes. + */ + default void registerCallback(StatusBarWindowCallback callback) {} + + /** Notifies the registered {@link StatusBarWindowCallback} instances. */ + default void notifyStateChangedCallbacks() {} + + /** + * Registers a listener to monitor scrims visibility. + * + * @param listener A listener to monitor scrims visibility + */ + default void setScrimsVisibilityListener(Consumer<Integer> listener) {} + + /** + * Adds the notification shade view to the window manager. + */ + default void attach() {} + + /** Sets the notification shade view. */ + default void setNotificationShadeView(ViewGroup view) {} + + /** Gets the notification shade view. */ + @Nullable + default ViewGroup getNotificationShadeView() { + return null; + } + + /** Sets the state of whether the keyguard is currently showing or not. */ + default void setKeyguardShowing(boolean showing) {} + + /** Sets the state of whether the keyguard is currently occluded or not. */ + default void setKeyguardOccluded(boolean occluded) {} + + /** Sets the state of whether the keyguard is currently needs input or not. */ + default void setKeyguardNeedsInput(boolean needsInput) {} + + /** Sets the state of whether the notification shade panel is currently visible or not. */ + default void setPanelVisible(boolean visible) {} + + /** Sets the state of whether the notification shade is focusable or not. */ + default void setNotificationShadeFocusable(boolean focusable) {} + + /** Sets the state of whether the bouncer is showing or not. */ + default void setBouncerShowing(boolean showing) {} + + /** Sets the state of whether the backdrop is showing or not. */ + default void setBackdropShowing(boolean showing) {} + + /** Sets the state of whether the keyguard is fading away or not. */ + default void setKeyguardFadingAway(boolean keyguardFadingAway) {} + + /** Sets the state of whether the quick settings is expanded or not. */ + default void setQsExpanded(boolean expanded) {} + + /** Sets the state of whether the user activities are forced or not. */ + default void setForceUserActivity(boolean forceUserActivity) {} + + /** Sets the state of whether the user activities are forced or not. */ + default void setLaunchingActivity(boolean launching) {} + + /** Sets the state of whether the scrim is visible or not. */ + default void setScrimsVisibility(int scrimsVisibility) {} + + /** Sets the background blur radius of the notification shade window. */ + default void setBackgroundBlurRadius(int backgroundBlurRadius) {} + + /** Sets the state of whether heads up is showing or not. */ + default void setHeadsUpShowing(boolean showing) {} + + /** Sets whether the wallpaper supports ambient mode or not. */ + default void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {} + + /** Gets whether the wallpaper is showing or not. */ + default boolean isShowingWallpaper() { + return false; + } + + /** Sets whether the window was collapsed by force or not. */ + default void setForceWindowCollapsed(boolean force) {} + + /** Sets whether panel is expanded or not. */ + default void setPanelExpanded(boolean isExpanded) {} + + /** Gets whether the panel is expanded or not. */ + default boolean getPanelExpanded() { + return false; + } + + /** Sets the state of whether the remote input is active or not. */ + default void onRemoteInputActive(boolean remoteInputActive) {} + + /** Sets the screen brightness level for when the device is dozing. */ + default void setDozeScreenBrightness(int value) {} + + /** + * Sets whether the screen brightness is forced to the value we use for doze mode by the status + * bar window. No-op if the device does not support dozing. + */ + default void setForceDozeBrightness(boolean forceDozeBrightness) {} + + /** Sets the state of whether sysui is dozing or not. */ + default void setDozing(boolean dozing) {} + + /** Sets the state of whether plugin open is forced or not. */ + default void setForcePluginOpen(boolean forcePluginOpen) {} + + /** Gets whether we are forcing plugin open or not. */ + default boolean getForcePluginOpen() { + return false; + } + + /** Sets the state of whether the notification shade is touchable or not. */ + default void setNotTouchable(boolean notTouchable) {} + + /** Sets a {@link OtherwisedCollapsedListener}. */ + default void setStateListener(OtherwisedCollapsedListener listener) {} + + /** Sets a {@link ForcePluginOpenListener}. */ + default void setForcePluginOpenListener(ForcePluginOpenListener listener) {} + + /** Sets whether the system is in a state where the keyguard is going away. */ + default void setKeyguardGoingAway(boolean goingAway) {} + + /** + * SystemUI may need top-ui to avoid jank when performing animations. After the + * animation is performed, the component should remove itself from the list of features that + * are forcing SystemUI to be top-ui. + */ + default void setRequestTopUi(boolean requestTopUi, String componentTag) {} + + /** + * Custom listener to pipe data back to plugins about whether or not the status bar would be + * collapsed if not for the plugin. + * TODO: Find cleaner way to do this. + */ + interface OtherwisedCollapsedListener { + void setWouldOtherwiseCollapse(boolean otherwiseCollapse); + } + + /** + * Listener to indicate forcePluginOpen has changed + */ + interface ForcePluginOpenListener { + /** + * Called when mState.forcePluginOpen is changed + */ + void onChange(boolean forceOpen); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 36f8bcdc9123..8f3033edecbb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -44,7 +44,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.ExpandableViewState; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.ViewState; import com.android.systemui.statusbar.phone.NotificationIconContainer; @@ -69,7 +69,7 @@ public class NotificationShelf extends ActivatableNotificationView implements private float mHiddenShelfIconSize; private int mStatusBarHeight; private AmbientState mAmbientState; - private NotificationStackScrollLayout mHostLayout; + private NotificationStackScrollLayoutController mHostLayoutController; private int mMaxLayoutHeight; private int mPaddingBetweenElements; private int mNotGoneIndex; @@ -114,9 +114,10 @@ public class NotificationShelf extends ActivatableNotificationView implements initDimens(); } - public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout) { + public void bind(AmbientState ambientState, + NotificationStackScrollLayoutController hostLayoutController) { mAmbientState = ambientState; - mHostLayout = hostLayout; + mHostLayoutController = hostLayoutController; } private void initDimens() { @@ -248,8 +249,8 @@ public class NotificationShelf extends ActivatableNotificationView implements float firstElementRoundness = 0.0f; ActivatableNotificationView previousAnv = null; - for (int i = 0; i < mHostLayout.getChildCount(); i++) { - ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i); + for (int i = 0; i < mHostLayoutController.getChildCount(); i++) { + ExpandableView child = (ExpandableView) mHostLayoutController.getChildAt(i); if (!child.needsClippingToShelf() || child.getVisibility() == GONE) { continue; @@ -354,8 +355,8 @@ public class NotificationShelf extends ActivatableNotificationView implements mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex()); mShelfIcons.calculateIconTranslations(); mShelfIcons.applyIconStates(); - for (int i = 0; i < mHostLayout.getChildCount(); i++) { - View child = mHostLayout.getChildAt(i); + for (int i = 0; i < mHostLayoutController.getChildCount(); i++) { + View child = mHostLayoutController.getChildAt(i); if (!(child instanceof ExpandableNotificationRow) || child.getVisibility() == GONE) { continue; @@ -378,8 +379,8 @@ public class NotificationShelf extends ActivatableNotificationView implements * swipes quickly. */ private void clipTransientViews() { - for (int i = 0; i < mHostLayout.getTransientViewCount(); i++) { - View transientView = mHostLayout.getTransientView(i); + for (int i = 0; i < mHostLayoutController.getTransientViewCount(); i++) { + View transientView = mHostLayoutController.getTransientView(i); if (transientView instanceof ExpandableView) { ExpandableView transientExpandableView = (ExpandableView) transientView; updateNotificationClipHeight(transientExpandableView, getTranslationY(), -1); @@ -618,7 +619,7 @@ public class NotificationShelf extends ActivatableNotificationView implements // We need to persist this, since after the expansion, the behavior should still be the // same. float position = mAmbientState.getIntrinsicPadding() - + mHostLayout.getPositionInLinearLayout(view); + + mHostLayoutController.getPositionInLinearLayout(view); int maxShelfStart = mMaxLayoutHeight - getIntrinsicHeight(); if (position < maxShelfStart && position + view.getIntrinsicHeight() >= maxShelfStart && view.getTranslationY() < position) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java index 9f8f8447d72c..77abcfa848ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java @@ -21,7 +21,7 @@ import android.view.View; import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController; import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope; import com.android.systemui.statusbar.notification.stack.AmbientState; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationIconContainer; import com.android.systemui.statusbar.phone.StatusBarNotificationPresenter; @@ -90,8 +90,8 @@ public class NotificationShelfController { } public void bind(AmbientState ambientState, - NotificationStackScrollLayout notificationStackScrollLayout) { - mView.bind(ambientState, notificationStackScrollLayout); + NotificationStackScrollLayoutController notificationStackScrollLayoutController) { + mView.bind(ambientState, notificationStackScrollLayoutController); mAmbientState = ambientState; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 02a8feec3fa9..1cd1b60ac1ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -21,7 +21,6 @@ import android.content.res.Resources; import android.os.Handler; import android.os.Trace; import android.os.UserHandle; -import android.service.notification.NotificationListenerService.Ranking; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -35,9 +34,9 @@ import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.DynamicChildBindController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; @@ -491,7 +490,6 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle } } - row.showAppOpsIcons(entry.mActiveAppOps); row.showFeedbackIcon(mAssistantFeedbackController.showFeedbackIndicator(entry)); row.setLastAudiblyAlertedMs(entry.getLastAudiblyAlertedMs()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java index 0a7ee3b0ebf0..2aba1038a97d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java @@ -28,8 +28,8 @@ import android.widget.TextView; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.settingslib.WirelessUtils; -import com.android.systemui.DemoMode; import com.android.systemui.Dependency; +import com.android.systemui.demomode.DemoModeCommandReceiver; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.policy.NetworkController; @@ -40,7 +40,8 @@ import com.android.systemui.tuner.TunerService.Tunable; import java.util.List; -public class OperatorNameView extends TextView implements DemoMode, DarkReceiver, +/** Shows the operator name */ +public class OperatorNameView extends TextView implements DemoModeCommandReceiver, DarkReceiver, SignalCallback, Tunable { private static final String KEY_SHOW_OPERATOR_NAME = "show_operator_name"; @@ -103,14 +104,18 @@ public class OperatorNameView extends TextView implements DemoMode, DarkReceiver @Override public void dispatchDemoCommand(String command, Bundle args) { - if (!mDemoMode && command.equals(COMMAND_ENTER)) { - mDemoMode = true; - } else if (mDemoMode && command.equals(COMMAND_EXIT)) { - mDemoMode = false; - update(); - } else if (mDemoMode && command.equals(COMMAND_OPERATOR)) { - setText(args.getString("name")); - } + setText(args.getString("name")); + } + + @Override + public void onDemoModeStarted() { + mDemoMode = true; + } + + @Override + public void onDemoModeFinished() { + mDemoMode = false; + update(); } private void update() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index 7b2585324b9f..ba54d1bff6e8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -30,24 +30,24 @@ import android.view.ViewConfiguration import com.android.systemui.Gefingerpoken import com.android.systemui.Interpolators import com.android.systemui.R +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.ShadeController import javax.inject.Inject -import javax.inject.Singleton import kotlin.math.max /** * A utility class to enable the downward swipe on when pulsing. */ -@Singleton +@SysUISingleton class PulseExpansionHandler @Inject constructor( context: Context, @@ -93,7 +93,7 @@ constructor( private set private val mTouchSlop: Float private lateinit var expansionCallback: ExpansionCallback - private lateinit var stackScroller: NotificationStackScrollLayout + private lateinit var stackScrollerController: NotificationStackScrollLayoutController private val mTemp2 = IntArray(2) private var mDraggedFarEnough: Boolean = false private var mStartingChild: ExpandableView? = null @@ -315,23 +315,23 @@ constructor( private fun findView(x: Float, y: Float): ExpandableView? { var totalX = x var totalY = y - stackScroller.getLocationOnScreen(mTemp2) + stackScrollerController.getLocationOnScreen(mTemp2) totalX += mTemp2[0].toFloat() totalY += mTemp2[1].toFloat() - val childAtRawPosition = stackScroller.getChildAtRawPosition(totalX, totalY) + val childAtRawPosition = stackScrollerController.getChildAtRawPosition(totalX, totalY) return if (childAtRawPosition != null && childAtRawPosition.isContentExpandable) { childAtRawPosition } else null } fun setUp( - stackScroller: NotificationStackScrollLayout, + stackScrollerController: NotificationStackScrollLayoutController, expansionCallback: ExpansionCallback, shadeController: ShadeController ) { this.expansionCallback = expansionCallback this.shadeController = shadeController - this.stackScroller = stackScroller + this.stackScrollerController = stackScrollerController } fun setPulsing(pulsing: Boolean) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index 2bef355d59f3..e9442499a8ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -23,11 +23,14 @@ import android.util.FloatProperty; import android.util.Log; import android.view.animation.Interpolator; +import androidx.annotation.NonNull; + import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.UiEventLogger; import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.Interpolators; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.policy.CallbackController; @@ -38,12 +41,11 @@ import java.util.ArrayList; import java.util.Comparator; import javax.inject.Inject; -import javax.inject.Singleton; /** * Tracks and reports on {@link StatusBarState}. */ -@Singleton +@SysUISingleton public class StatusBarStateControllerImpl implements SysuiStatusBarStateController, CallbackController<StateListener>, Dumpable { private static final String TAG = "SbStateController"; @@ -101,6 +103,11 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll private boolean mIsDozing; /** + * If the status bar is currently expanded or not. + */ + private boolean mIsExpanded; + + /** * Current {@link #mDozeAmount} animator. */ private ValueAnimator mDarkAnimator; @@ -188,6 +195,26 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll } @Override + public boolean isExpanded() { + return mIsExpanded; + } + + @Override + public boolean setPanelExpanded(boolean expanded) { + if (mIsExpanded == expanded) { + return false; + } + mIsExpanded = expanded; + String tag = getClass().getSimpleName() + "#setIsExpanded"; + DejankUtils.startDetectingBlockingIpcs(tag); + for (RankedListener rl : new ArrayList<>(mListeners)) { + rl.mListener.onExpandedChanged(mIsExpanded); + } + DejankUtils.stopDetectingBlockingIpcs(tag); + return true; + } + + @Override public float getInterpolatedDozeAmount() { return mDozeInterpolator.getInterpolation(mDozeAmount); } @@ -276,7 +303,7 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll } @Override - public void addCallback(StateListener listener) { + public void addCallback(@NonNull StateListener listener) { synchronized (mListeners) { addListenerInternalLocked(listener, Integer.MAX_VALUE); } @@ -316,7 +343,7 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll @Override - public void removeCallback(StateListener listener) { + public void removeCallback(@NonNull StateListener listener) { synchronized (mListeners) { mListeners.removeIf((it) -> it.mListener.equals(listener)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java b/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java index 27e4adee68ba..1ec043cb7670 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java @@ -21,6 +21,7 @@ import android.view.LayoutInflater; import android.view.ViewGroup; import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent; import com.android.systemui.statusbar.phone.LockIcon; import com.android.systemui.statusbar.phone.LockscreenLockIconController; @@ -30,13 +31,12 @@ import com.android.systemui.statusbar.phone.StatusBarWindowView; import com.android.systemui.util.InjectionInflationController; import javax.inject.Inject; -import javax.inject.Singleton; /** * Creates a single instance of super_status_bar and super_notification_shade that can be shared * across various system ui objects. */ -@Singleton +@SysUISingleton public class SuperStatusBarViewFactory { private final Context mContext; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java index 07b35502478f..9f8fe35dfbc9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java @@ -75,6 +75,14 @@ public interface SysuiStatusBarStateController extends StatusBarStateController */ void setDozeAmount(float dozeAmount, boolean animated); + + /** + * Update the expanded state from {@link StatusBar}'s perspective + * @param expanded are we expanded? + * @return {@code true} if the state changed, else {@code false} + */ + boolean setPanelExpanded(boolean expanded); + /** * Sets whether to leave status bar open when hiding keyguard */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java index 442416fd3bea..ea90bdd940a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java @@ -26,12 +26,13 @@ import android.os.VibrationEffect; import android.os.Vibrator; import android.provider.Settings; +import com.android.systemui.dagger.SysUISingleton; + import javax.inject.Inject; -import javax.inject.Singleton; /** */ -@Singleton +@SysUISingleton public class VibratorHelper { private final Vibrator mVibrator; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java index 992f2da5c3b5..44550b72e521 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java @@ -22,6 +22,7 @@ import android.os.Handler; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.media.MediaDataManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -33,26 +34,24 @@ import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.DynamicChildBindController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationGroupManager; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.concurrency.DelayableExecutor; -import javax.inject.Singleton; - import dagger.Lazy; import dagger.Module; import dagger.Provides; @@ -65,7 +64,7 @@ import dagger.Provides; @Module public interface StatusBarDependenciesModule { /** */ - @Singleton + @SysUISingleton @Provides static NotificationRemoteInputManager provideNotificationRemoteInputManager( Context context, @@ -92,7 +91,7 @@ public interface StatusBarDependenciesModule { } /** */ - @Singleton + @SysUISingleton @Provides static NotificationMediaManager provideNotificationMediaManager( Context context, @@ -117,7 +116,7 @@ public interface StatusBarDependenciesModule { } /** */ - @Singleton + @SysUISingleton @Provides static NotificationListener provideNotificationListener( Context context, @@ -128,7 +127,7 @@ public interface StatusBarDependenciesModule { } /** */ - @Singleton + @SysUISingleton @Provides static SmartReplyController provideSmartReplyController( NotificationEntryManager entryManager, @@ -138,7 +137,7 @@ public interface StatusBarDependenciesModule { } /** */ - @Singleton + @SysUISingleton @Provides static NotificationViewHierarchyManager provideNotificationViewHierarchyManager( Context context, @@ -176,7 +175,7 @@ public interface StatusBarDependenciesModule { * Provides our instance of CommandQueue which is considered optional. */ @Provides - @Singleton + @SysUISingleton static CommandQueue provideCommandQueue(Context context, ProtoTracer protoTracer) { return new CommandQueue(context, protoTracer); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java index b813b6280c12..87a3f0755ec5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java @@ -30,10 +30,10 @@ import android.provider.Settings; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import javax.inject.Inject; -import javax.inject.Singleton; /** * Determines whether to show any indicators or controls related to notification assistant. @@ -41,7 +41,7 @@ import javax.inject.Singleton; * Flags protect any changes from being shown. Notifications that are adjusted by the assistant * should show an indicator. */ -@Singleton +@SysUISingleton public class AssistantFeedbackController extends ContentObserver { private final Uri FEEDBACK_URI = Settings.Global.getUriFor(Settings.Global.NOTIFICATION_FEEDBACK_ENABLED); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt index 1972b869ba25..c68625c9d9ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt @@ -24,6 +24,7 @@ import android.service.notification.NotificationListenerService.Ranking import android.service.notification.NotificationListenerService.RankingMap import com.android.internal.statusbar.NotificationVisibility import com.android.internal.widget.ConversationLayout +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow @@ -32,7 +33,6 @@ import com.android.systemui.statusbar.notification.stack.StackStateAnimator import com.android.systemui.statusbar.phone.NotificationGroupManager import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject -import javax.inject.Singleton /** Populates additional information in conversation notifications */ class ConversationNotificationProcessor @Inject constructor( @@ -61,7 +61,7 @@ class ConversationNotificationProcessor @Inject constructor( * Tracks state related to conversation notifications, and updates the UI of existing notifications * when necessary. */ -@Singleton +@SysUISingleton class ConversationNotificationManager @Inject constructor( private val notificationEntryManager: NotificationEntryManager, private val notificationGroupManager: NotificationGroupManager, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java index 2ab329e9574c..9482c17a8769 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java @@ -16,8 +16,10 @@ package com.android.systemui.statusbar.notification; +import android.annotation.Nullable; import android.util.ArraySet; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.StatusBarState; @@ -25,22 +27,21 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import javax.inject.Inject; -import javax.inject.Singleton; /** * A controller which dynamically controls the visibility of Notification content */ -@Singleton +@SysUISingleton public class DynamicPrivacyController implements KeyguardStateController.Callback { private final KeyguardStateController mKeyguardStateController; private final NotificationLockscreenUserManager mLockscreenUserManager; private final StatusBarStateController mStateController; - private ArraySet<Listener> mListeners = new ArraySet<>(); + private final ArraySet<Listener> mListeners = new ArraySet<>(); private boolean mLastDynamicUnlocked; private boolean mCacheInvalid; - private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + @Nullable private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Inject DynamicPrivacyController(NotificationLockscreenUserManager notificationLockscreenUserManager, @@ -96,8 +97,7 @@ public class DynamicPrivacyController implements KeyguardStateController.Callbac * contents aren't revealed yet? */ public boolean isInLockedDownShade() { - if (!mStatusBarKeyguardViewManager.isShowing() - || !mKeyguardStateController.isMethodSecure()) { + if (!isStatusBarKeyguardShowing() || !mKeyguardStateController.isMethodSecure()) { return false; } int state = mStateController.getState(); @@ -110,6 +110,10 @@ public class DynamicPrivacyController implements KeyguardStateController.Callbac return true; } + private boolean isStatusBarKeyguardShowing() { + return mStatusBarKeyguardViewManager != null && mStatusBarKeyguardViewManager.isShowing(); + } + public void setStatusBarKeyguardViewManager( StatusBarKeyguardViewManager statusBarKeyguardViewManager) { mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt index dfc2fc1c4584..314051c8ce6c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt @@ -19,9 +19,9 @@ package com.android.systemui.statusbar.notification import android.content.Context import android.provider.DeviceConfig import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_ALLOW_FGS_DISMISSAL +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.util.DeviceConfigProxy import javax.inject.Inject -import javax.inject.Singleton private var sIsEnabled: Boolean? = null @@ -29,7 +29,7 @@ private var sIsEnabled: Boolean? = null * Feature controller for NOTIFICATIONS_ALLOW_FGS_DISMISSAL config. */ // TODO: this is really boilerplatey, make a base class that just wraps the device config -@Singleton +@SysUISingleton class ForegroundServiceDismissalFeatureController @Inject constructor( val proxy: DeviceConfigProxy, val context: Context diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java index 312b2c52b42e..ea614fb210b3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java @@ -53,6 +53,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; @@ -63,12 +64,11 @@ import java.util.List; import java.util.concurrent.Executor; import javax.inject.Inject; -import javax.inject.Singleton; /** The class to show notification(s) of instant apps. This may show multiple notifications on * splitted screen. */ -@Singleton +@SysUISingleton public class InstantAppNotifier extends SystemUI implements CommandQueue.Callbacks, KeyguardStateController.Callback { private static final String TAG = "InstantAppNotifier"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index e1ff872456eb..b5f1c7ff9b62 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -48,6 +48,7 @@ import com.android.systemui.statusbar.NotificationUiAdjustment; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java index 6335a09cde2b..590ccf830a78 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java @@ -29,6 +29,7 @@ import android.service.notification.StatusBarNotification; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.ForegroundServiceController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -37,13 +38,12 @@ import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; import javax.inject.Inject; -import javax.inject.Singleton; /** Component which manages the various reasons a notification might be filtered out.*/ // TODO: delete NotificationFilter.java after migrating to new NotifPipeline b/145659174. // Notification filtering is taken care of across the different Coordinators (mostly // KeyguardCoordinator.java) -@Singleton +@SysUISingleton public class NotificationFilter { private final NotificationGroupManager mGroupManager = Dependency.get( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index f982cf05de97..1326d920fe42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -19,22 +19,21 @@ package com.android.systemui.statusbar.notification import android.animation.ObjectAnimator import android.util.FloatProperty import com.android.systemui.Interpolators +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.notification.collection.NotificationEntry -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.notification.stack.StackStateAnimator import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.KeyguardBypassController -import com.android.systemui.statusbar.phone.NotificationIconAreaController import com.android.systemui.statusbar.phone.PanelExpansionListener import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener - import javax.inject.Inject -import javax.inject.Singleton +import kotlin.math.min -@Singleton +@SysUISingleton class NotificationWakeUpCoordinator @Inject constructor( private val mHeadsUpManager: HeadsUpManager, private val statusBarStateController: StatusBarStateController, @@ -53,7 +52,7 @@ class NotificationWakeUpCoordinator @Inject constructor( return coordinator.mLinearVisibilityAmount } } - private lateinit var mStackScroller: NotificationStackScrollLayout + private lateinit var mStackScrollerController: NotificationStackScrollLayoutController private var mVisibilityInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE private var mLinearDozeAmount: Float = 0.0f @@ -79,7 +78,7 @@ class NotificationWakeUpCoordinator @Inject constructor( if (mNotificationsVisible && !mNotificationsVisibleForExpansion && !bypassController.bypassEnabled) { // We're waking up while pulsing, let's make sure the animation looks nice - mStackScroller.wakeUpFromPulse() + mStackScrollerController.wakeUpFromPulse() } if (bypassController.bypassEnabled && !mNotificationsVisible) { // Let's make sure our huns become visible once we are waking up in case @@ -98,7 +97,6 @@ class NotificationWakeUpCoordinator @Inject constructor( } private var collapsedEnoughToHide: Boolean = false - lateinit var iconAreaController: NotificationIconAreaController var pulsing: Boolean = false set(value) { @@ -156,10 +154,10 @@ class NotificationWakeUpCoordinator @Inject constructor( }) } - fun setStackScroller(stackScroller: NotificationStackScrollLayout) { - mStackScroller = stackScroller - pulseExpanding = stackScroller.isPulseExpanding - stackScroller.setOnPulseHeightChangedListener { + fun setStackScroller(stackScrollerController: NotificationStackScrollLayoutController) { + mStackScrollerController = stackScrollerController + pulseExpanding = stackScrollerController.isPulseExpanding + stackScrollerController.setOnPulseHeightChangedListener { val nowExpanding = isPulseExpanding() val changed = nowExpanding != pulseExpanding pulseExpanding = nowExpanding @@ -169,7 +167,7 @@ class NotificationWakeUpCoordinator @Inject constructor( } } - fun isPulseExpanding(): Boolean = mStackScroller.isPulseExpanding + fun isPulseExpanding(): Boolean = mStackScrollerController.isPulseExpanding /** * @param visible should notifications be visible @@ -249,7 +247,7 @@ class NotificationWakeUpCoordinator @Inject constructor( val changed = linear != mLinearDozeAmount mLinearDozeAmount = linear mDozeAmount = eased - mStackScroller.setDozeAmount(mDozeAmount) + mStackScrollerController.setDozeAmount(mDozeAmount) updateHideAmount() if (changed && linear == 0.0f) { setNotificationsVisible(visible = false, animate = false, increaseSpeed = false) @@ -330,18 +328,18 @@ class NotificationWakeUpCoordinator @Inject constructor( } fun getWakeUpHeight(): Float { - return mStackScroller.wakeUpHeight + return mStackScrollerController.wakeUpHeight } private fun updateHideAmount() { - val linearAmount = Math.min(1.0f - mLinearVisibilityAmount, mLinearDozeAmount) - val amount = Math.min(1.0f - mVisibilityAmount, mDozeAmount) - mStackScroller.setHideAmount(linearAmount, amount) + val linearAmount = min(1.0f - mLinearVisibilityAmount, mLinearDozeAmount) + val amount = min(1.0f - mVisibilityAmount, mDozeAmount) + mStackScrollerController.setHideAmount(linearAmount, amount) notificationsFullyHidden = linearAmount == 1.0f } private fun notifyAnimationStart(awake: Boolean) { - mStackScroller.notifyHideAnimationStart(!awake) + mStackScrollerController.notifyHideAnimationStart(!awake) } override fun onDozingChanged(isDozing: Boolean) { @@ -355,7 +353,7 @@ class NotificationWakeUpCoordinator @Inject constructor( * from a pulse and determines how much the notifications are expanded. */ fun setPulseHeight(height: Float): Float { - val overflow = mStackScroller.setPulseHeight(height) + val overflow = mStackScrollerController.setPulseHeight(height) // no overflow for the bypass experience return if (bypassController.bypassEnabled) 0.0f else overflow } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt index 57f8a6a3abef..17e62890aadd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt @@ -46,7 +46,14 @@ data class ListAttachState private constructor( /** * The [NotifPromoter] promoting this entry to top-level, if any. Always null for [GroupEntry]s. */ - var promoter: NotifPromoter? + var promoter: NotifPromoter?, + + /** + * If the [VisualStabilityManager] is suppressing group or section changes for this entry, + * suppressedChanges will contain the new parent or section that we would have assigned to + * the entry had it not been suppressed by the VisualStabilityManager. + */ + var suppressedChanges: SuppressedAttachState ) { /** Copies the state of another instance. */ @@ -56,6 +63,7 @@ data class ListAttachState private constructor( sectionIndex = other.sectionIndex excludingFilter = other.excludingFilter promoter = other.promoter + suppressedChanges.clone(other.suppressedChanges) } /** Resets back to a "clean" state (the same as created by the factory method) */ @@ -65,6 +73,7 @@ data class ListAttachState private constructor( sectionIndex = -1 excludingFilter = null promoter = null + suppressedChanges.reset() } companion object { @@ -75,7 +84,8 @@ data class ListAttachState private constructor( null, -1, null, - null) + null, + SuppressedAttachState.create()) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java index 3a0520115d67..786c97d03712 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java @@ -167,6 +167,23 @@ public class ListDumper { .append(" "); } + if (notifEntry.getAttachState().getSuppressedChanges().getParent() != null) { + rksb.append("suppressedParent=") + .append(notifEntry.getAttachState().getSuppressedChanges() + .getParent().getKey()) + .append(" "); + } + + if (notifEntry.getAttachState().getSuppressedChanges().getSection() != null) { + rksb.append("suppressedSectionIndex=") + .append(notifEntry.getAttachState().getSuppressedChanges() + .getSectionIndex()) + .append(" sectionName=") + .append(notifEntry.getAttachState().getSuppressedChanges() + .getSection().getName()) + .append(" "); + } + if (hasBeenInteractedWith) { rksb.append("interacted=yes "); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java index 285cf7abce20..90492b5d606d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java @@ -59,6 +59,7 @@ import androidx.annotation.NonNull; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.LogBufferEulogizer; import com.android.systemui.statusbar.FeatureFlags; @@ -98,7 +99,6 @@ import java.util.Queue; import java.util.concurrent.TimeUnit; import javax.inject.Inject; -import javax.inject.Singleton; /** * Keeps a record of all of the "active" notifications, i.e. the notifications that are currently @@ -123,7 +123,7 @@ import javax.inject.Singleton; * events occur. */ @MainThread -@Singleton +@SysUISingleton public class NotifCollection implements Dumpable { private final IStatusBarService mStatusBarService; private final SystemClock mClock; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java index aaf5c4d6594b..8562a2e55a4f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.collection; import com.android.internal.statusbar.IStatusBarService; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; @@ -24,14 +25,13 @@ import com.android.systemui.statusbar.notification.row.NotifInflationErrorManage import com.android.systemui.statusbar.notification.row.NotificationContentInflater; import javax.inject.Inject; -import javax.inject.Singleton; /** * Handles notification inflating, rebinding, and inflation aborting. * * Currently a wrapper for NotificationRowBinderImpl. */ -@Singleton +@SysUISingleton public class NotifInflaterImpl implements NotifInflater { private final IStatusBarService mStatusBarService; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java index 17899e99275b..05dd4df1f2ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.collection; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener; @@ -24,6 +25,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor; @@ -33,7 +35,6 @@ import java.util.Collection; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * The system that constructs the "shade list", the filtered, grouped, and sorted list of @@ -68,7 +69,7 @@ import javax.inject.Singleton; * 9. OnBeforeRenderListListeners are fired ({@link #addOnBeforeRenderListListener}) * 9. The list is handed off to the view layer to be rendered */ -@Singleton +@SysUISingleton public class NotifPipeline implements CommonNotifCollection { private final NotifCollection mNotifCollection; private final ShadeListBuilder mShadeListBuilder; @@ -161,6 +162,14 @@ public class NotifPipeline implements CommonNotifCollection { } /** + * StabilityManager that is used to determine whether to suppress group and section changes. + * This should only be set once. + */ + public void setVisualStabilityManager(NotifStabilityManager notifStabilityManager) { + mShadeListBuilder.setNotifStabilityManager(notifStabilityManager); + } + + /** * Comparators that are used to sort top-level entries that share the same section. The * comparators are executed in order until one of them returns a non-zero result. If all return * zero, the pipeline falls back to sorting by rank (and, failing that, Notification.when). diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java index d45f89cb6fe5..6cbebf803511 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java @@ -21,6 +21,7 @@ import static com.android.systemui.statusbar.notification.collection.listbuilder import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_FINALIZE_FILTERING; import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_FINALIZING; import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_GROUPING; +import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_GROUP_STABILIZING; import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_IDLE; import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_PRE_GROUP_FILTERING; import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_RESETTING; @@ -35,6 +36,7 @@ import android.util.Pair; import androidx.annotation.NonNull; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.NotificationInteractionTracker; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener; @@ -47,6 +49,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener; import com.android.systemui.util.Assert; @@ -63,7 +66,6 @@ import java.util.Map; import java.util.Objects; import javax.inject.Inject; -import javax.inject.Singleton; /** * The second half of {@link NotifPipeline}. Sits downstream of the NotifCollection and transforms @@ -71,7 +73,7 @@ import javax.inject.Singleton; * notifications that are currently present in the notification shade. */ @MainThread -@Singleton +@SysUISingleton public class ShadeListBuilder implements Dumpable { private final SystemClock mSystemClock; private final ShadeListBuilderLogger mLogger; @@ -90,6 +92,7 @@ public class ShadeListBuilder implements Dumpable { private final List<NotifFilter> mNotifFinalizeFilters = new ArrayList<>(); private final List<NotifComparator> mNotifComparators = new ArrayList<>(); private final List<NotifSection> mNotifSections = new ArrayList<>(); + @Nullable private NotifStabilityManager mNotifStabilityManager; private final List<OnBeforeTransformGroupsListener> mOnBeforeTransformGroupsListeners = new ArrayList<>(); @@ -109,7 +112,8 @@ public class ShadeListBuilder implements Dumpable { SystemClock systemClock, ShadeListBuilderLogger logger, DumpManager dumpManager, - NotificationInteractionTracker interactionTracker) { + NotificationInteractionTracker interactionTracker + ) { Assert.isMainThread(); mSystemClock = systemClock; mLogger = logger; @@ -200,6 +204,22 @@ public class ShadeListBuilder implements Dumpable { } } + void setNotifStabilityManager(NotifStabilityManager notifStabilityManager) { + Assert.isMainThread(); + mPipelineState.requireState(STATE_IDLE); + + if (mNotifStabilityManager != null) { + throw new IllegalStateException( + "Attempting to set the NotifStabilityManager more than once. There should " + + "only be one visual stability manager. Manager is being set by " + + mNotifStabilityManager.getName() + " and " + + notifStabilityManager.getName()); + } + + mNotifStabilityManager = notifStabilityManager; + mNotifStabilityManager.setInvalidationListener(this::onReorderingAllowedInvalidated); + } + void setComparators(List<NotifComparator> comparators) { Assert.isMainThread(); mPipelineState.requireState(STATE_IDLE); @@ -237,6 +257,16 @@ public class ShadeListBuilder implements Dumpable { rebuildListIfBefore(STATE_PRE_GROUP_FILTERING); } + private void onReorderingAllowedInvalidated(NotifStabilityManager stabilityManager) { + Assert.isMainThread(); + + mLogger.logReorderingAllowedInvalidated( + stabilityManager.getName(), + mPipelineState.getState()); + + rebuildListIfBefore(STATE_GROUPING); + } + private void onPromoterInvalidated(NotifPromoter promoter) { Assert.isMainThread(); @@ -285,6 +315,7 @@ public class ShadeListBuilder implements Dumpable { // Step 1: Reset notification states mPipelineState.incrementTo(STATE_RESETTING); resetNotifs(); + onBeginRun(); // Step 2: Filter out any notifications that shouldn't be shown right now mPipelineState.incrementTo(STATE_PRE_GROUP_FILTERING); @@ -303,6 +334,10 @@ public class ShadeListBuilder implements Dumpable { promoteNotifs(mNotifList); pruneIncompleteGroups(mNotifList); + // Step 4.5: Reassign/revert any groups to maintain visual stability + mPipelineState.incrementTo(STATE_GROUP_STABILIZING); + stabilizeGroupingNotifs(mNotifList); + // Step 5: Sort // Assign each top-level entry a section, then sort the list by section and then within // section by our list of custom comparators @@ -472,6 +507,28 @@ public class ShadeListBuilder implements Dumpable { } } + private void stabilizeGroupingNotifs(List<ListEntry> list) { + if (mNotifStabilityManager == null) { + return; + } + + for (int i = 0; i < list.size(); i++) { + final ListEntry tle = list.get(i); + if (tle.getPreviousAttachState().getParent() == null) { + continue; // new entries are allowed + } + + final GroupEntry prevParent = tle.getPreviousAttachState().getParent(); + final GroupEntry assignedParent = tle.getParent(); + if (prevParent != assignedParent) { + if (!mNotifStabilityManager.isGroupChangeAllowed(tle.getRepresentativeEntry())) { + tle.getAttachState().getSuppressedChanges().setParent(assignedParent); + tle.setParent(prevParent); + } + } + } + } + private void promoteNotifs(List<ListEntry> list) { for (int i = 0; i < list.size(); i++) { final ListEntry tle = list.get(i); @@ -650,9 +707,18 @@ public class ShadeListBuilder implements Dumpable { mLogger.logParentChanged(mIterationCount, prev.getParent(), curr.getParent()); } + if (curr.getSuppressedChanges().getParent() != null) { + mLogger.logParentChangeSuppressed( + mIterationCount, + curr.getSuppressedChanges().getParent(), + curr.getParent()); + } + if (curr.getExcludingFilter() != prev.getExcludingFilter()) { mLogger.logFilterChanged( - mIterationCount, prev.getExcludingFilter(), curr.getExcludingFilter()); + mIterationCount, + prev.getExcludingFilter(), + curr.getExcludingFilter()); } // When something gets detached, its promoter and section are always set to null, so @@ -661,7 +727,9 @@ public class ShadeListBuilder implements Dumpable { if (!wasDetached && curr.getPromoter() != prev.getPromoter()) { mLogger.logPromoterChanged( - mIterationCount, prev.getPromoter(), curr.getPromoter()); + mIterationCount, + prev.getPromoter(), + curr.getPromoter()); } if (!wasDetached && curr.getSection() != prev.getSection()) { @@ -672,6 +740,20 @@ public class ShadeListBuilder implements Dumpable { curr.getSection(), curr.getSectionIndex()); } + + if (curr.getSuppressedChanges().getSection() != null) { + mLogger.logSectionChangeSuppressed( + mIterationCount, + curr.getSuppressedChanges().getSection(), + curr.getSuppressedChanges().getSectionIndex(), + curr.getSection()); + } + } + } + + private void onBeginRun() { + if (mNotifStabilityManager != null) { + mNotifStabilityManager.onBeginRun(); } } @@ -681,6 +763,10 @@ public class ShadeListBuilder implements Dumpable { callOnCleanup(mNotifFinalizeFilters); callOnCleanup(mNotifComparators); callOnCleanup(mNotifSections); + + if (mNotifStabilityManager != null) { + callOnCleanup(List.of(mNotifStabilityManager)); + } } private void callOnCleanup(List<? extends Pluggable<?>> pluggables) { @@ -770,12 +856,32 @@ public class ShadeListBuilder implements Dumpable { } private Pair<NotifSection, Integer> applySections(ListEntry entry) { - final Pair<NotifSection, Integer> sectionWithIndex = findSection(entry); - final NotifSection section = sectionWithIndex.first; - final Integer sectionIndex = sectionWithIndex.second; + Pair<NotifSection, Integer> sectionWithIndex = findSection(entry); + final ListAttachState prevAttachState = entry.getPreviousAttachState(); + + // are we changing sections of this entry? + if (mNotifStabilityManager != null + && prevAttachState.getParent() != null + && (sectionWithIndex.first != prevAttachState.getSection() + || sectionWithIndex.second != prevAttachState.getSectionIndex())) { + + // are section changes allowed? + if (!mNotifStabilityManager.isSectionChangeAllowed( + entry.getRepresentativeEntry())) { + entry.getAttachState().getSuppressedChanges().setSection( + sectionWithIndex.first); + entry.getAttachState().getSuppressedChanges().setSectionIndex( + sectionWithIndex.second); + + // keep the previous section + sectionWithIndex = new Pair( + prevAttachState.getSection(), + prevAttachState.getSectionIndex()); + } + } - entry.getAttachState().setSection(section); - entry.getAttachState().setSectionIndex(sectionIndex); + entry.getAttachState().setSection(sectionWithIndex.first); + entry.getAttachState().setSectionIndex(sectionWithIndex.second); return sectionWithIndex; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt new file mode 100644 index 000000000000..52612365712e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.statusbar.notification.collection + +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection + +/** + * Stores the suppressed state that [ShadeListBuilder] assigned to this [ListEntry] before the + * VisualStabilityManager suppressed group and section changes. + */ +data class SuppressedAttachState private constructor( + /** + * Null if not attached to the current shade list. If top-level, then the shade list root. If + * part of a group, then that group's GroupEntry. + */ + var parent: GroupEntry?, + + /** + * The assigned section for this ListEntry. If the child of the group, this will be the + * parent's section. Null if not attached to the list. + */ + var section: NotifSection?, + var sectionIndex: Int +) { + + /** Copies the state of another instance. */ + fun clone(other: SuppressedAttachState) { + parent = other.parent + section = other.section + sectionIndex = other.sectionIndex + } + + /** Resets back to a "clean" state (the same as created by the factory method) */ + fun reset() { + parent = null + section = null + sectionIndex = -1 + } + + companion object { + @JvmStatic + fun create(): SuppressedAttachState { + return SuppressedAttachState( + null, + null, + -1) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt index a0f9dc91ce68..5dc0dcc4d717 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt @@ -20,13 +20,13 @@ import android.content.Context import android.content.pm.PackageManager import android.service.notification.StatusBarNotification import android.util.Log +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.statusbar.phone.StatusBar import javax.inject.Inject -import javax.inject.Singleton -@Singleton +@SysUISingleton class TargetSdkResolver @Inject constructor( private val context: Context ) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java index 68ec6b620a53..0b9bded5ef58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java @@ -19,28 +19,24 @@ package com.android.systemui.statusbar.notification.collection.coordinator; import static android.app.NotificationManager.IMPORTANCE_MIN; import android.app.Notification; -import android.os.UserHandle; import android.service.notification.StatusBarNotification; -import android.util.ArraySet; import com.android.systemui.ForegroundServiceController; import com.android.systemui.appops.AppOpsController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; -import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; -import com.android.systemui.util.Assert; import com.android.systemui.util.concurrency.DelayableExecutor; import java.util.HashMap; import java.util.Map; import javax.inject.Inject; -import javax.inject.Singleton; /** * Handles ForegroundService and AppOp interactions with notifications. @@ -55,7 +51,7 @@ import javax.inject.Singleton; * frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener * frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender */ -@Singleton +@SysUISingleton public class AppOpsCoordinator implements Coordinator { private static final String TAG = "AppOpsCoordinator"; @@ -82,14 +78,9 @@ public class AppOpsCoordinator implements Coordinator { // extend the lifetime of foreground notification services to show for at least 5 seconds mNotifPipeline.addNotificationLifetimeExtender(mForegroundLifetimeExtender); - // listen for new notifications to add appOps - mNotifPipeline.addCollectionListener(mNotifCollectionListener); - // filter out foreground service notifications that aren't necessary anymore mNotifPipeline.addPreGroupFilter(mNotifFilter); - // when appOps change, update any relevant notifications to update appOps for - mAppOpsController.addCallback(ForegroundServiceController.APP_OPS, this::onAppOpsChanged); } public NotifSection getSection() { @@ -186,35 +177,6 @@ public class AppOpsCoordinator implements Coordinator { }; /** - * Adds appOps to incoming and updating notifications - */ - private NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() { - @Override - public void onEntryAdded(NotificationEntry entry) { - tagAppOps(entry); - } - - @Override - public void onEntryUpdated(NotificationEntry entry) { - tagAppOps(entry); - } - - private void tagAppOps(NotificationEntry entry) { - final StatusBarNotification sbn = entry.getSbn(); - // note: requires that the ForegroundServiceController is updating their appOps first - ArraySet<Integer> activeOps = - mForegroundServiceController.getAppOps( - sbn.getUser().getIdentifier(), - sbn.getPackageName()); - - entry.mActiveAppOps.clear(); - if (activeOps != null) { - entry.mActiveAppOps.addAll(activeOps); - } - } - }; - - /** * Puts foreground service notifications into its own section. */ private final NotifSection mNotifSection = new NotifSection("ForegroundService") { @@ -230,53 +192,4 @@ public class AppOpsCoordinator implements Coordinator { return false; } }; - - private void onAppOpsChanged(int code, int uid, String packageName, boolean active) { - mMainExecutor.execute(() -> handleAppOpsChanged(code, uid, packageName, active)); - } - - /** - * Update the appOp for the posted notification associated with the current foreground service - * - * @param code code for appOp to add/remove - * @param uid of user the notification is sent to - * @param packageName package that created the notification - * @param active whether the appOpCode is active or not - */ - private void handleAppOpsChanged(int code, int uid, String packageName, boolean active) { - Assert.isMainThread(); - - int userId = UserHandle.getUserId(uid); - - // Update appOps of the app's posted notifications with standard layouts - final ArraySet<String> notifKeys = - mForegroundServiceController.getStandardLayoutKeys(userId, packageName); - if (notifKeys != null) { - boolean changed = false; - for (int i = 0; i < notifKeys.size(); i++) { - final NotificationEntry entry = findNotificationEntryWithKey(notifKeys.valueAt(i)); - if (entry != null - && uid == entry.getSbn().getUid() - && packageName.equals(entry.getSbn().getPackageName())) { - if (active) { - changed |= entry.mActiveAppOps.add(code); - } else { - changed |= entry.mActiveAppOps.remove(code); - } - } - } - if (changed) { - mNotifFilter.invalidateList(); - } - } - } - - private NotificationEntry findNotificationEntryWithKey(String key) { - for (NotificationEntry entry : mNotifPipeline.getAllNotifs()) { - if (entry.getKey().equals(key)) { - return entry; - } - } - return null; - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java index 08462c183bd9..4ddc1dc8498d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator; import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -28,7 +29,6 @@ import java.util.HashSet; import java.util.Set; import javax.inject.Inject; -import javax.inject.Singleton; /** * Coordinates hiding, intercepting (the dismissal), and deletion of bubbled notifications. @@ -50,7 +50,7 @@ import javax.inject.Singleton; * respond to app-cancellations (ie: remove the bubble if the app cancels the notification). * */ -@Singleton +@SysUISingleton public class BubbleCoordinator implements Coordinator { private static final String TAG = "BubbleCoordinator"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt index 1a9de8829faf..c8e859f27a5c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -24,14 +25,13 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON import javax.inject.Inject -import javax.inject.Singleton /** * A Conversation/People Coordinator that: * - Elevates important conversation notifications * - Puts conversations into its own people section. @see [NotifCoordinators] for section ordering. */ -@Singleton +@SysUISingleton class ConversationCoordinator @Inject constructor( private val peopleNotificationIdentifier: PeopleNotificationIdentifier ) : Coordinator { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java index 625d1b9686e9..47928b42ed5e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java @@ -23,20 +23,20 @@ import android.content.pm.PackageManager; import android.os.RemoteException; import android.service.notification.StatusBarNotification; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import javax.inject.Inject; -import javax.inject.Singleton; /** * Filters out most notifications when the device is unprovisioned. * Special notifications with extra permissions and tags won't be filtered out even when the * device is unprovisioned. */ -@Singleton +@SysUISingleton public class DeviceProvisionedCoordinator implements Coordinator { private static final String TAG = "DeviceProvisionedCoordinator"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java index 72597afc3b90..6e6cecaf62fa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java @@ -21,6 +21,7 @@ import static com.android.systemui.statusbar.notification.interruption.HeadsUpCo import android.annotation.Nullable; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; @@ -37,7 +38,6 @@ import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import java.util.Objects; import javax.inject.Inject; -import javax.inject.Singleton; /** * Coordinates heads up notification (HUN) interactions with the notification pipeline based on @@ -53,7 +53,7 @@ import javax.inject.Singleton; * * Note: The inflation callback in {@link PreparationCoordinator} handles showing HUNs. */ -@Singleton +@SysUISingleton public class HeadsUpCoordinator implements Coordinator { private static final String TAG = "HeadsUpCoordinator"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java index 95ba759df547..318cdb171fa5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java @@ -34,6 +34,7 @@ import androidx.annotation.MainThread; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.notification.NotificationUtils; @@ -46,12 +47,11 @@ import com.android.systemui.statusbar.notification.collection.provider.HighPrior import com.android.systemui.statusbar.policy.KeyguardStateController; import javax.inject.Inject; -import javax.inject.Singleton; /** * Filters low priority and privacy-sensitive notifications from the lockscreen. */ -@Singleton +@SysUISingleton public class KeyguardCoordinator implements Coordinator { private static final String TAG = "KeyguardCoordinator"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java index a09c6509e65e..87ca717982f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.notification.collection.NotifPipeline; @@ -31,17 +32,17 @@ import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * Handles the attachment of {@link Coordinator}s to the {@link NotifPipeline} so that the * Coordinators can register their respective callbacks. */ -@Singleton +@SysUISingleton public class NotifCoordinators implements Dumpable { private static final String TAG = "NotifCoordinators"; private final List<Coordinator> mCoordinators = new ArrayList<>(); private final List<NotifSection> mOrderedSections = new ArrayList<>(); + /** * Creates all the coordinators. */ @@ -58,7 +59,8 @@ public class NotifCoordinators implements Dumpable { HeadsUpCoordinator headsUpCoordinator, ConversationCoordinator conversationCoordinator, PreparationCoordinator preparationCoordinator, - MediaCoordinator mediaCoordinator) { + MediaCoordinator mediaCoordinator, + VisualStabilityCoordinator visualStabilityCoordinator) { dumpManager.registerDumpable(TAG, this); mCoordinators.add(new HideLocallyDismissedNotifsCoordinator()); @@ -70,6 +72,7 @@ public class NotifCoordinators implements Dumpable { mCoordinators.add(bubbleCoordinator); mCoordinators.add(mediaCoordinator); mCoordinators.add(conversationCoordinator); + mCoordinators.add(visualStabilityCoordinator); if (featureFlags.isNewNotifPipelineRenderingEnabled()) { mCoordinators.add(headsUpCoordinator); mCoordinators.add(preparationCoordinator); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java index ee2bb6b1d190..31826c7219de 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java @@ -28,6 +28,7 @@ import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.collection.GroupEntry; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; @@ -48,7 +49,6 @@ import java.util.Map; import java.util.Set; import javax.inject.Inject; -import javax.inject.Singleton; /** * Kicks off core notification inflation and view rebinding when a notification is added or updated. @@ -57,7 +57,7 @@ import javax.inject.Singleton; * If a notification was uninflated, this coordinator will filter the notification out from the * {@link ShadeListBuilder} until it is inflated. */ -@Singleton +@SysUISingleton public class PreparationCoordinator implements Coordinator { private static final String TAG = "PreparationCoordinator"; @@ -307,7 +307,7 @@ public class PreparationCoordinator implements Coordinator { private void onInflationFinished(NotificationEntry entry) { mLogger.logNotifInflated(entry.getKey()); mInflatingNotifs.remove(entry); - mViewBarn.registerViewForEntry(entry, entry.getRow()); + mViewBarn.registerViewForEntry(entry, entry.getRowController()); mInflationStates.put(entry, STATE_INFLATED); mNotifInflatingFilter.invalidateList(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java index 0d2f9da77db7..a32b1636057b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; @@ -25,7 +26,6 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import javax.inject.Inject; -import javax.inject.Singleton; /** * Filters out NotificationEntries based on its Ranking and dozing state. @@ -34,7 +34,7 @@ import javax.inject.Singleton; * - whether the notification's app is suspended or hiding its notifications * - whether DND settings are hiding notifications from ambient display or the notification list */ -@Singleton +@SysUISingleton public class RankingCoordinator implements Coordinator { private static final String TAG = "RankingNotificationCoordinator"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java new file mode 100644 index 000000000000..08030f8201a2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.statusbar.notification.collection.coordinator; + +import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; +import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING; + +import android.annotation.NonNull; + +import androidx.annotation.VisibleForTesting; + +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.NotificationViewHierarchyManager; +import com.android.systemui.statusbar.notification.collection.NotifPipeline; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; +import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.util.concurrency.DelayableExecutor; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; + +/** + * Ensures that notifications are visually stable if the user is looking at the notifications. + * Group and section changes are re-allowed when the notification entries are no longer being + * viewed. + * + * Previously this was implemented in the view-layer {@link NotificationViewHierarchyManager} by + * {@link com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager}. + * This is now integrated in the data-layer via + * {@link com.android.systemui.statusbar.notification.collection.ShadeListBuilder}. + */ +@SysUISingleton +public class VisualStabilityCoordinator implements Coordinator { + private final DelayableExecutor mDelayableExecutor; + private final WakefulnessLifecycle mWakefulnessLifecycle; + private final StatusBarStateController mStatusBarStateController; + private final HeadsUpManager mHeadsUpManager; + + private boolean mScreenOn; + private boolean mPanelExpanded; + private boolean mPulsing; + + private boolean mReorderingAllowed; + private boolean mIsSuppressingGroupChange = false; + private final Set<String> mEntriesWithSuppressedSectionChange = new HashSet<>(); + + // key: notification key that can temporarily change its section + // value: runnable that when run removes its associated RemoveOverrideSuppressionRunnable + // from the DelayableExecutor's queue + private Map<String, Runnable> mEntriesThatCanChangeSection = new HashMap<>(); + + @VisibleForTesting + protected static final long ALLOW_SECTION_CHANGE_TIMEOUT = 500; + + @Inject + public VisualStabilityCoordinator( + HeadsUpManager headsUpManager, + WakefulnessLifecycle wakefulnessLifecycle, + StatusBarStateController statusBarStateController, + DelayableExecutor delayableExecutor + ) { + mHeadsUpManager = headsUpManager; + mWakefulnessLifecycle = wakefulnessLifecycle; + mStatusBarStateController = statusBarStateController; + mDelayableExecutor = delayableExecutor; + } + + @Override + public void attach(NotifPipeline pipeline) { + mWakefulnessLifecycle.addObserver(mWakefulnessObserver); + mScreenOn = mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE + || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING; + + mStatusBarStateController.addCallback(mStatusBarStateControllerListener); + mPulsing = mStatusBarStateController.isPulsing(); + + pipeline.setVisualStabilityManager(mNotifStabilityManager); + } + + private final NotifStabilityManager mNotifStabilityManager = + new NotifStabilityManager("VisualStabilityCoordinator") { + @Override + public void onBeginRun() { + mIsSuppressingGroupChange = false; + mEntriesWithSuppressedSectionChange.clear(); + } + + @Override + public boolean isGroupChangeAllowed(NotificationEntry entry) { + final boolean isGroupChangeAllowedForEntry = + mReorderingAllowed || mHeadsUpManager.isAlerting(entry.getKey()); + mIsSuppressingGroupChange |= isGroupChangeAllowedForEntry; + return isGroupChangeAllowedForEntry; + } + + @Override + public boolean isSectionChangeAllowed(NotificationEntry entry) { + final boolean isSectionChangeAllowedForEntry = + mReorderingAllowed + || mHeadsUpManager.isAlerting(entry.getKey()) + || mEntriesThatCanChangeSection.containsKey(entry.getKey()); + if (isSectionChangeAllowedForEntry) { + mEntriesWithSuppressedSectionChange.add(entry.getKey()); + } + return isSectionChangeAllowedForEntry; + } + }; + + private void updateAllowedStates() { + mReorderingAllowed = isReorderingAllowed(); + if (mReorderingAllowed && (mIsSuppressingGroupChange || isSuppressingSectionChange())) { + mNotifStabilityManager.invalidateList(); + } + } + + private boolean isSuppressingSectionChange() { + return !mEntriesWithSuppressedSectionChange.isEmpty(); + } + + private boolean isReorderingAllowed() { + return (!mScreenOn || !mPanelExpanded) && !mPulsing; + } + + /** + * Allows this notification entry to be re-ordered in the notification list temporarily until + * the timeout has passed. + * + * Typically this is allowed because the user has directly changed something about the + * notification and we are reordering based on the user's change. + * + * @param entry notification entry that can change sections even if isReorderingAllowed is false + * @param now current time SystemClock.uptimeMillis + */ + public void temporarilyAllowSectionChanges(@NonNull NotificationEntry entry, long now) { + final String entryKey = entry.getKey(); + final boolean wasSectionChangeAllowed = + mNotifStabilityManager.isSectionChangeAllowed(entry); + + // If it exists, cancel previous timeout + if (mEntriesThatCanChangeSection.containsKey(entryKey)) { + mEntriesThatCanChangeSection.get(entryKey).run(); + } + + // Schedule & store new timeout cancellable + mEntriesThatCanChangeSection.put( + entryKey, + mDelayableExecutor.executeAtTime( + () -> mEntriesThatCanChangeSection.remove(entryKey), + now + ALLOW_SECTION_CHANGE_TIMEOUT)); + + if (!wasSectionChangeAllowed) { + mNotifStabilityManager.invalidateList(); + } + } + + final StatusBarStateController.StateListener mStatusBarStateControllerListener = + new StatusBarStateController.StateListener() { + @Override + public void onPulsingChanged(boolean pulsing) { + mPulsing = pulsing; + updateAllowedStates(); + } + + @Override + public void onExpandedChanged(boolean expanded) { + mPanelExpanded = expanded; + updateAllowedStates(); + } + }; + + final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() { + @Override + public void onFinishedGoingToSleep() { + mScreenOn = false; + updateAllowedStates(); + } + + @Override + public void onStartedWakingUp() { + mScreenOn = true; + updateAllowedStates(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java index 73c0fdc56b8d..6089aa26fe71 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.collection.inflation; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.notification.collection.GroupEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -25,13 +26,12 @@ import com.android.systemui.statusbar.notification.row.RowContentBindStage; import com.android.systemui.statusbar.phone.NotificationGroupManager; import javax.inject.Inject; -import javax.inject.Singleton; /** * Helper class that provide methods to help check when we need to inflate a low priority version * ot notification content. */ -@Singleton +@SysUISingleton public class LowPriorityInflationHelper { private final FeatureFlags mFeatureFlags; private final NotificationGroupManager mGroupManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index 8849824380d3..7c061aad7b4a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -24,6 +24,7 @@ import android.os.Build; import android.view.ViewGroup; import com.android.internal.util.NotificationMessagingUtil; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -44,10 +45,9 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain import javax.inject.Inject; import javax.inject.Provider; -import javax.inject.Singleton; /** Handles inflating and updating views for notifications. */ -@Singleton +@SysUISingleton public class NotificationRowBinderImpl implements NotificationRowBinder { private static final String TAG = "NotificationViewManager"; @@ -136,6 +136,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { .expandableNotificationRow(row) .notificationEntry(entry) .onExpandClickListener(mPresenter) + .listContainer(mListContainer) .build(); ExpandableNotificationRowController rowController = component.getExpandableNotificationRowController(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnDismissCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java index 36adf9b87674..19a356b89f18 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnDismissCallbackImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.collection.inflation; import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; +import android.os.SystemClock; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationStats; @@ -26,35 +27,43 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.logging.NotificationLogger; -import com.android.systemui.statusbar.notification.row.OnDismissCallback; +import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.policy.HeadsUpManager; /** - * Callback used when a user: - * 1. Manually dismisses a notification {@see ExpandableNotificationRow}. - * 2. Clicks on a notification with flag {@link android.app.Notification#FLAG_AUTO_CANCEL}. - * {@see StatusBarNotificationActivityStarter} + * Callback for when a user interacts with a {@see ExpandableNotificationRow}. Sends relevant + * information about the interaction to the notification pipeline. */ -public class OnDismissCallbackImpl implements OnDismissCallback { +public class OnUserInteractionCallbackImpl implements OnUserInteractionCallback { private final NotifPipeline mNotifPipeline; private final NotifCollection mNotifCollection; private final HeadsUpManager mHeadsUpManager; private final StatusBarStateController mStatusBarStateController; + private final VisualStabilityCoordinator mVisualStabilityCoordinator; - public OnDismissCallbackImpl( + public OnUserInteractionCallbackImpl( NotifPipeline notifPipeline, NotifCollection notifCollection, HeadsUpManager headsUpManager, - StatusBarStateController statusBarStateController + StatusBarStateController statusBarStateController, + VisualStabilityCoordinator visualStabilityCoordinator ) { mNotifPipeline = notifPipeline; mNotifCollection = notifCollection; mHeadsUpManager = headsUpManager; mStatusBarStateController = statusBarStateController; + mVisualStabilityCoordinator = visualStabilityCoordinator; } + /** + * Callback triggered when a user: + * 1. Manually dismisses a notification {@see ExpandableNotificationRow}. + * 2. Clicks on a notification with flag {@link android.app.Notification#FLAG_AUTO_CANCEL}. + * {@see StatusBarNotificationActivityStarter} + */ @Override public void onDismiss( NotificationEntry entry, @@ -80,4 +89,11 @@ public class OnDismissCallbackImpl implements OnDismissCallback { NotificationLogger.getNotificationLocation(entry))) ); } + + @Override + public void onImportanceChanged(NotificationEntry entry) { + mVisualStabilityCoordinator.temporarilyAllowSectionChanges( + entry, + SystemClock.uptimeMillis()); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java index 9782c3ef55fe..db49e4476a99 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.collection.init; import android.util.Log; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationListener; @@ -29,20 +30,18 @@ import com.android.systemui.statusbar.notification.collection.ShadeListBuilder; import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer; import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; -import com.android.systemui.statusbar.notification.collection.render.NotifViewManager; -import com.android.systemui.statusbar.notification.collection.render.NotifViewManagerBuilder; +import com.android.systemui.statusbar.notification.collection.render.ShadeViewManagerFactory; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import java.io.FileDescriptor; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Singleton; /** * Initialization code for the new notification pipeline. */ -@Singleton +@SysUISingleton public class NotifPipelineInitializer implements Dumpable { private final NotifPipeline mPipelineWrapper; private final GroupCoalescer mGroupCoalescer; @@ -51,7 +50,7 @@ public class NotifPipelineInitializer implements Dumpable { private final NotifCoordinators mNotifPluggableCoordinators; private final NotifInflaterImpl mNotifInflater; private final DumpManager mDumpManager; - private final NotifViewManagerBuilder mNotifViewManagerBuilder; + private final ShadeViewManagerFactory mShadeViewManagerFactory; private final FeatureFlags mFeatureFlags; @@ -64,7 +63,7 @@ public class NotifPipelineInitializer implements Dumpable { NotifCoordinators notifCoordinators, NotifInflaterImpl notifInflater, DumpManager dumpManager, - NotifViewManagerBuilder notifViewManagerBuilder, + ShadeViewManagerFactory shadeViewManagerFactory, FeatureFlags featureFlags) { mPipelineWrapper = pipelineWrapper; mGroupCoalescer = groupCoalescer; @@ -73,8 +72,8 @@ public class NotifPipelineInitializer implements Dumpable { mNotifPluggableCoordinators = notifCoordinators; mDumpManager = dumpManager; mNotifInflater = notifInflater; + mShadeViewManagerFactory = shadeViewManagerFactory; mFeatureFlags = featureFlags; - mNotifViewManagerBuilder = notifViewManagerBuilder; } /** Hooks the new pipeline up to NotificationManager */ @@ -95,8 +94,7 @@ public class NotifPipelineInitializer implements Dumpable { // Wire up pipeline if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { - NotifViewManager notifViewManager = mNotifViewManagerBuilder.build(listContainer); - notifViewManager.attach(mListBuilder); + mShadeViewManagerFactory.create(listContainer).attach(mListBuilder); } mListBuilder.attach(mNotifCollection); mNotifCollection.attach(mGroupCoalescer); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnDismissCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java index 94ffa8f8c5ed..cce8cdc64d30 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnDismissCallbackImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java @@ -27,30 +27,36 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.logging.NotificationLogger; -import com.android.systemui.statusbar.notification.row.OnDismissCallback; +import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.policy.HeadsUpManager; /** - * Callback used when a user: - * 1. Manually dismisses a notification {@see ExpandableNotificationRow}. - * 2. Clicks on a notification with flag {@link android.app.Notification#FLAG_AUTO_CANCEL}. - * {@see StatusBarNotificationActivityStarter} + * Callback for when a user interacts with a {@see ExpandableNotificationRow}. */ -public class OnDismissCallbackImpl implements OnDismissCallback { +public class OnUserInteractionCallbackImplLegacy implements OnUserInteractionCallback { private final NotificationEntryManager mNotificationEntryManager; private final HeadsUpManager mHeadsUpManager; private final StatusBarStateController mStatusBarStateController; + private final VisualStabilityManager mVisualStabilityManager; - public OnDismissCallbackImpl( + public OnUserInteractionCallbackImplLegacy( NotificationEntryManager notificationEntryManager, HeadsUpManager headsUpManager, - StatusBarStateController statusBarStateController + StatusBarStateController statusBarStateController, + VisualStabilityManager visualStabilityManager ) { mNotificationEntryManager = notificationEntryManager; mHeadsUpManager = headsUpManager; mStatusBarStateController = statusBarStateController; + mVisualStabilityManager = visualStabilityManager; } + /** + * Callback triggered when a user: + * 1. Manually dismisses a notification {@see ExpandableNotificationRow}. + * 2. Clicks on a notification with flag {@link android.app.Notification#FLAG_AUTO_CANCEL}. + * {@see StatusBarNotificationActivityStarter} + */ @Override public void onDismiss( NotificationEntry entry, @@ -77,5 +83,10 @@ public class OnDismissCallbackImpl implements OnDismissCallback { cancellationReason ); } + + @Override + public void onImportanceChanged(NotificationEntry entry) { + mVisualStabilityManager.temporarilyAllowReordering(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/VisualStabilityManager.java index 8341c02b6b63..165df30b457d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/VisualStabilityManager.java @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.statusbar.notification; +package com.android.systemui.statusbar.notification.collection.legacy; import android.os.Handler; import android.os.SystemClock; @@ -24,7 +24,11 @@ import androidx.collection.ArraySet; import com.android.systemui.Dumpable; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.statusbar.NotificationPresenter; +import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.notification.NotificationEntryListener; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.VisibilityLocationProvider; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.dagger.NotificationsModule; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -65,24 +69,44 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl */ public VisualStabilityManager( NotificationEntryManager notificationEntryManager, - @Main Handler handler) { + @Main Handler handler, + StatusBarStateController statusBarStateController, + WakefulnessLifecycle wakefulnessLifecycle) { mHandler = handler; - notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { - @Override - public void onPreEntryUpdated(NotificationEntry entry) { - final boolean ambientStateHasChanged = - entry.isAmbient() != entry.getRow().isLowPriority(); - if (ambientStateHasChanged) { - // note: entries are removed in onReorderingFinished - mLowPriorityReorderingViews.add(entry); + if (notificationEntryManager != null) { + notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { + @Override + public void onPreEntryUpdated(NotificationEntry entry) { + final boolean ambientStateHasChanged = + entry.isAmbient() != entry.getRow().isLowPriority(); + if (ambientStateHasChanged) { + // note: entries are removed in onReorderingFinished + mLowPriorityReorderingViews.add(entry); + } } - } - }); - } + }); + } + + if (statusBarStateController != null) { + setPulsing(statusBarStateController.isPulsing()); + statusBarStateController.addCallback(new StatusBarStateController.StateListener() { + @Override + public void onPulsingChanged(boolean pulsing) { + setPulsing(pulsing); + } + + @Override + public void onExpandedChanged(boolean expanded) { + setPanelExpanded(expanded); + } + }); + } - public void setUpWithPresenter(NotificationPresenter presenter) { + if (wakefulnessLifecycle != null) { + wakefulnessLifecycle.addObserver(mWakefulnessObserver); + } } /** @@ -120,25 +144,25 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl } /** - * Set the panel to be expanded. + * @param screenOn whether the screen is on */ - public void setPanelExpanded(boolean expanded) { - mPanelExpanded = expanded; + private void setScreenOn(boolean screenOn) { + mScreenOn = screenOn; updateAllowedStates(); } /** - * @param screenOn whether the screen is on + * Set the panel to be expanded. */ - public void setScreenOn(boolean screenOn) { - mScreenOn = screenOn; + private void setPanelExpanded(boolean expanded) { + mPanelExpanded = expanded; updateAllowedStates(); } /** * @param pulsing whether we are currently pulsing for ambient display. */ - public void setPulsing(boolean pulsing) { + private void setPulsing(boolean pulsing) { if (mPulsing == pulsing) { return; } @@ -215,6 +239,10 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl mVisibilityLocationProvider = visibilityLocationProvider; } + /** + * Notifications have been reordered, so reset all the allowed list of views that are allowed + * to reorder. + */ public void onReorderingFinished() { mAllowedReorderViews.clear(); mAddedChildren.clear(); @@ -271,11 +299,27 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl pw.println(); } + final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() { + @Override + public void onFinishedGoingToSleep() { + setScreenOn(false); + } + + @Override + public void onStartedWakingUp() { + setScreenOn(true); + } + }; + + + /** + * See {@link Callback#onChangeAllowed()} + */ public interface Callback { + /** * Called when changing is allowed again. */ void onChangeAllowed(); } - } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java index f1f7d632b6f8..798bfe7f39d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java @@ -81,9 +81,10 @@ public class PipelineState { public static final int STATE_PRE_GROUP_FILTERING = 3; public static final int STATE_GROUPING = 4; public static final int STATE_TRANSFORMING = 5; - public static final int STATE_SORTING = 6; - public static final int STATE_FINALIZE_FILTERING = 7; - public static final int STATE_FINALIZING = 8; + public static final int STATE_GROUP_STABILIZING = 6; + public static final int STATE_SORTING = 7; + public static final int STATE_FINALIZE_FILTERING = 8; + public static final int STATE_FINALIZING = 9; @IntDef(prefix = { "STATE_" }, value = { STATE_IDLE, @@ -92,6 +93,7 @@ public class PipelineState { STATE_PRE_GROUP_FILTERING, STATE_GROUPING, STATE_TRANSFORMING, + STATE_GROUP_STABILIZING, STATE_SORTING, STATE_FINALIZE_FILTERING, STATE_FINALIZING, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt index 67f8bfeaf141..f7bfeb7234f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt @@ -57,6 +57,15 @@ class ShadeListBuilderLogger @Inject constructor( }) } + fun logReorderingAllowedInvalidated(name: String, pipelineState: Int) { + buffer.log(TAG, DEBUG, { + str1 = name + int1 = pipelineState + }, { + """ReorderingNowAllowed "$str1" invalidated; pipeline state is $int1""" + }) + } + fun logPromoterInvalidated(name: String, pipelineState: Int) { buffer.log(TAG, DEBUG, { str1 = name @@ -156,6 +165,21 @@ class ShadeListBuilderLogger @Inject constructor( }) } + fun logParentChangeSuppressed( + buildId: Int, + suppressedParent: GroupEntry?, + keepingParent: GroupEntry? + ) { + buffer.log(TAG, INFO, { + int1 = buildId + str1 = suppressedParent?.key + str2 = keepingParent?.key + }, { + "(Build $long1) Change of parent to '$str1' suppressed; " + + "keeping parent '$str2'" + }) + } + fun logFilterChanged( buildId: Int, prevFilter: NotifFilter?, @@ -206,6 +230,23 @@ class ShadeListBuilderLogger @Inject constructor( }) } + fun logSectionChangeSuppressed( + buildId: Int, + suppressedSection: NotifSection?, + suppressedSectionIndex: Int, + assignedSection: NotifSection? + ) { + buffer.log(TAG, INFO, { + long1 = buildId.toLong() + str1 = suppressedSection?.name + int1 = suppressedSectionIndex + str2 = assignedSection?.name + }, { + "(Build $long1) Section change suppressed: '$str1' (#$int1). " + + "Keeping section: '$str2'" + }) + } + fun logFinalList(entries: List<ListEntry>) { if (entries.isEmpty()) { buffer.log(TAG, DEBUG, {}, { "(empty list)" }) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java new file mode 100644 index 000000000000..58d4b97f69b5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.statusbar.notification.collection.listbuilder.pluggable; + +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +/** + * Pluggable for participating in notif stabilization. In particular, suppressing group and + * section changes. + * + * The stability manager should be invalidated when previously suppressed a group or + * section change is now allowed. + */ +public abstract class NotifStabilityManager extends Pluggable<NotifStabilityManager> { + + protected NotifStabilityManager(String name) { + super(name); + } + + /** + * Called at the beginning of every pipeline run to perform any necessary cleanup from the + * previous run. + */ + public abstract void onBeginRun(); + + /** + * Returns whether this notification can currently change groups/parents. + * Per iteration of the notification pipeline, locally stores this information until the next + * run of the pipeline. When this method returns true, it's expected that a group change for + * this entry is being suppressed. + */ + public abstract boolean isGroupChangeAllowed(NotificationEntry entry); + + /** + * Returns whether this notification entry can currently change sections. + * Per iteration of the notification pipeline, locally stores this information until the next + * run of the pipeline. When this method returns true, it's expected that a section change is + * being suppressed. + */ + public abstract boolean isSectionChangeAllowed(NotificationEntry entry); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java index 1c076c49249b..8b803b517f14 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.collection.provider; import android.app.Notification; import android.app.NotificationManager; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.collection.GroupEntry; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -28,7 +29,6 @@ import com.android.systemui.statusbar.phone.NotificationGroupManager; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * Determines whether a notification is considered 'high priority'. @@ -36,7 +36,7 @@ import javax.inject.Singleton; * Notifications that are high priority are visible on the lock screen/status bar and in the top * section in the shade. */ -@Singleton +@SysUISingleton public class HighPriorityProvider { private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; private final NotificationGroupManager mGroupManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt new file mode 100644 index 000000000000..67f7b1c09df3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.statusbar.notification.collection.render + +import android.view.View +import java.lang.RuntimeException +import java.lang.StringBuilder + +/** + * A controller that represents a single unit of addable/removable view(s) in the notification + * shade. Some nodes are just a single view (such as a header), while some might involve many views + * (such as a notification row). + * + * It's possible for nodes to support having child nodes (for example, some notification rows + * contain other notification rows). If so, they must implement all of the child-related methods + * below. + */ +interface NodeController { + /** A string that uniquely(ish) represents the node in the tree. Used for debugging. */ + val nodeLabel: String + + val view: View + + fun getChildAt(index: Int): View? { + throw RuntimeException("Not supported") + } + + fun getChildCount(): Int { + throw RuntimeException("Not supported") + } + + fun addChildAt(child: NodeController, index: Int) { + throw RuntimeException("Not supported") + } + + fun moveChildTo(child: NodeController, index: Int) { + throw RuntimeException("Not supported") + } + + fun removeChild(child: NodeController, isTransfer: Boolean) { + throw RuntimeException("Not supported") + } +} + +/** + * Used to specify the tree of [NodeController]s that currently make up the shade. + */ +interface NodeSpec { + val parent: NodeSpec? + val controller: NodeController + val children: List<NodeSpec> +} + +class NodeSpecImpl( + override val parent: NodeSpec?, + override val controller: NodeController +) : NodeSpec { + override val children = mutableListOf<NodeSpec>() +} + +/** + * Converts a tree spec to human-readable string, for dumping purposes. + */ +fun treeSpecToStr(tree: NodeSpec): String { + return StringBuilder().also { treeSpecToStrHelper(tree, it, "") }.toString() +} + +private fun treeSpecToStrHelper(tree: NodeSpec, sb: StringBuilder, indent: String) { + sb.append("${indent}ns{${tree.controller.nodeLabel}") + if (tree.children.isNotEmpty()) { + val childIndent = "$indent " + for (child in tree.children) { + treeSpecToStrHelper(child, sb, childIndent) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt index 54000950e3ef..79bc3d757ebd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt @@ -17,19 +17,20 @@ package com.android.systemui.statusbar.notification.collection.render import android.view.textclassifier.Log +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.collection.ListEntry -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController import javax.inject.Inject -import javax.inject.Singleton /** - * The ViewBarn is just a map from [ListEntry] to an instance of an [ExpandableNotificationRow]. + * The ViewBarn is just a map from [ListEntry] to an instance of an + * [ExpandableNotificationRowController]. */ -@Singleton +@SysUISingleton class NotifViewBarn @Inject constructor() { - private val rowMap = mutableMapOf<String, ExpandableNotificationRow>() + private val rowMap = mutableMapOf<String, ExpandableNotificationRowController>() - fun requireView(forEntry: ListEntry): ExpandableNotificationRow { + fun requireView(forEntry: ListEntry): ExpandableNotificationRowController { if (DEBUG) { Log.d(TAG, "requireView: $forEntry.key") } @@ -41,11 +42,11 @@ class NotifViewBarn @Inject constructor() { return li } - fun registerViewForEntry(entry: ListEntry, view: ExpandableNotificationRow) { + fun registerViewForEntry(entry: ListEntry, controller: ExpandableNotificationRowController) { if (DEBUG) { Log.d(TAG, "registerViewForEntry: $entry.key") } - rowMap[entry.key] = view + rowMap[entry.key] = controller } fun removeViewForEntry(entry: ListEntry) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewManager.kt deleted file mode 100644 index f2e2c39ab612..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewManager.kt +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (C) 2020 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 com.android.systemui.statusbar.notification.collection.render - -import android.annotation.MainThread -import android.view.View -import com.android.systemui.statusbar.notification.collection.GroupEntry -import com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY -import com.android.systemui.statusbar.notification.collection.ListEntry -import com.android.systemui.statusbar.notification.collection.NotificationEntry -import com.android.systemui.statusbar.notification.collection.ShadeListBuilder -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow -import com.android.systemui.statusbar.notification.stack.NotificationListContainer -import javax.inject.Inject - -/** - * A consumer of a Notification tree built by [ShadeListBuilder] which will update the notification - * presenter with the minimum operations required to make the old tree match the new one - */ -@MainThread -class NotifViewManager constructor( - private val listContainer: NotificationListContainer, - private val viewBarn: NotifViewBarn, - private val logger: NotifViewManagerLogger -) { - private val rootNode = RootWrapper(listContainer) - private val rows = mutableMapOf<ListEntry, RowNode>() - - fun attach(listBuilder: ShadeListBuilder) { - listBuilder.setOnRenderListListener(::onNewNotifTree) - } - - private fun onNewNotifTree(tree: List<ListEntry>) { - // Step 1: Detach all views whose parents have changed - detachRowsWithModifiedParents() - - // Step 2: Attach all new views and reattach all views whose parents changed. - // Also reorder existing children to match the spec we've received - val orderChanged = addAndReorderChildren(rootNode, tree) - if (orderChanged) { - listContainer.generateChildOrderChangedEvent() - } - } - - private fun detachRowsWithModifiedParents() { - val toRemove = mutableListOf<ListEntry>() - for (row in rows.values) { - val oldParentEntry = row.nodeParent?.entry - val newParentEntry = row.entry.parent - - if (newParentEntry != oldParentEntry) { - // If the parent is null, then we should remove the child completely. If not, then - // the parent merely changed: we'll detach it for now and then attach it to the - // new parent in step 2. - val isTransfer = newParentEntry != null - if (!isTransfer) { - toRemove.add(row.entry) - } - - if (!isTransfer && !isAttachedToRootEntry(oldParentEntry)) { - // If our view parent has also been removed (i.e. is no longer attached to the - // root entry) then we skip removing the child here - logger.logSkippingDetach(row.entry.key, row.nodeParent?.entry?.key) - } else { - logger.logDetachingChild( - row.entry.key, - isTransfer, - oldParentEntry?.key, - newParentEntry?.key) - row.nodeParent?.removeChild(row, isTransfer) - row.nodeParent = null - } - } - } - rows.keys.removeAll(toRemove) - } - - private fun addAndReorderChildren(parent: ParentNode, childEntries: List<ListEntry>): Boolean { - var orderChanged = false - for ((index, entry) in childEntries.withIndex()) { - val row = getRowNode(entry) - val currView = parent.getChildViewAt(index) - if (currView != row.view) { - when (row.nodeParent) { - null -> { - logger.logAttachingChild(row.entry.key, parent.entry.key) - parent.addChildAt(row, index) - row.nodeParent = parent - } - parent -> { - logger.logMovingChild(row.entry.key, parent.entry.key, index) - parent.moveChild(row, index) - orderChanged = true - } - else -> { - throw IllegalStateException("Child ${row.entry.key} should have parent " + - "${parent.entry.key} but is actually " + - "${row.nodeParent?.entry?.key}") - } - } - } - if (row is GroupWrapper) { - val childOrderChanged = addAndReorderChildren(row, row.entry.children) - orderChanged = orderChanged || childOrderChanged - } - } - // TODO: setUntruncatedChildCount - - return orderChanged - } - - private fun getRowNode(entry: ListEntry): RowNode { - return rows.getOrPut(entry) { - when (entry) { - is NotificationEntry -> RowWrapper(entry, viewBarn.requireView(entry)) - is GroupEntry -> - GroupWrapper( - entry, - viewBarn.requireView(checkNotNull(entry.summary)), - listContainer) - else -> throw RuntimeException( - "Unexpected entry type for ${entry.key}: ${entry.javaClass}") - } - } - } -} - -class NotifViewManagerBuilder @Inject constructor( - private val viewBarn: NotifViewBarn, - private val logger: NotifViewManagerLogger -) { - fun build(listContainer: NotificationListContainer): NotifViewManager { - return NotifViewManager(listContainer, viewBarn, logger) - } -} - -private fun isAttachedToRootEntry(entry: ListEntry?): Boolean { - return when (entry) { - null -> false - ROOT_ENTRY -> true - else -> isAttachedToRootEntry(entry.parent) - } -} - -private interface Node { - val entry: ListEntry - val nodeParent: ParentNode? -} - -private interface ParentNode : Node { - fun getChildViewAt(index: Int): View? - fun addChildAt(child: RowNode, index: Int) - fun moveChild(child: RowNode, index: Int) - fun removeChild(child: RowNode, isTransfer: Boolean) -} - -private interface RowNode : Node { - val view: ExpandableNotificationRow - override var nodeParent: ParentNode? -} - -private class RootWrapper( - private val listContainer: NotificationListContainer -) : ParentNode { - override val entry: ListEntry = ROOT_ENTRY - override val nodeParent: ParentNode? = null - - override fun getChildViewAt(index: Int): View? { - return listContainer.getContainerChildAt(index) - } - - override fun addChildAt(child: RowNode, index: Int) { - listContainer.addContainerViewAt(child.view, index) - } - - override fun moveChild(child: RowNode, index: Int) { - listContainer.changeViewPosition(child.view, index) - } - - override fun removeChild(child: RowNode, isTransfer: Boolean) { - if (isTransfer) { - listContainer.setChildTransferInProgress(true) - } - listContainer.removeContainerView(child.view) - if (isTransfer) { - listContainer.setChildTransferInProgress(false) - } - } -} - -private class GroupWrapper( - override val entry: GroupEntry, - override val view: ExpandableNotificationRow, - val listContainer: NotificationListContainer -) : RowNode, ParentNode { - - override var nodeParent: ParentNode? = null - - override fun getChildViewAt(index: Int): View? { - return view.getChildNotificationAt(index) - } - - override fun addChildAt(child: RowNode, index: Int) { - view.addChildNotification(child.view, index) - listContainer.notifyGroupChildAdded(child.view) - } - - override fun moveChild(child: RowNode, index: Int) { - view.removeChildNotification(child.view) - view.addChildNotification(child.view, index) - } - - override fun removeChild(child: RowNode, isTransfer: Boolean) { - view.removeChildNotification(child.view) - if (isTransfer) { - listContainer.notifyGroupChildRemoved(child.view, view) - } - } -} - -private class RowWrapper( - override val entry: NotificationEntry, - override val view: ExpandableNotificationRow -) : RowNode { - override var nodeParent: ParentNode? = null -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt new file mode 100644 index 000000000000..e8124944bcb0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.statusbar.notification.collection.render + +import android.view.View +import com.android.systemui.statusbar.notification.row.ExpandableView +import com.android.systemui.statusbar.notification.stack.NotificationListContainer + +/** + * Temporary wrapper around [NotificationListContainer], for use by [ShadeViewDiffer]. Long term, + * we should just modify NLC to implement the NodeController interface. + */ +class RootNodeController( + private val listContainer: NotificationListContainer +) : NodeController { + override val nodeLabel: String = "<root>" + override val view: View = listContainer as View + + override fun getChildAt(index: Int): View? { + return listContainer.getContainerChildAt(index) + } + + override fun getChildCount(): Int { + return listContainer.containerChildCount + } + + override fun addChildAt(child: NodeController, index: Int) { + listContainer.addContainerViewAt(child.view, index) + } + + override fun moveChildTo(child: NodeController, index: Int) { + listContainer.changeViewPosition(child.view as ExpandableView, index) + } + + override fun removeChild(child: NodeController, isTransfer: Boolean) { + if (isTransfer) { + listContainer.setChildTransferInProgress(true) + } + listContainer.removeContainerView(child.view) + if (isTransfer) { + listContainer.setChildTransferInProgress(false) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt new file mode 100644 index 000000000000..019520f18982 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.statusbar.notification.collection.render + +import android.annotation.MainThread +import android.view.View +import com.android.systemui.util.kotlin.transform + +/** + * Given a "spec" that describes a "tree" of views, adds and removes views from the + * [rootController] and its children until the actual tree matches the spec. + * + * Every node in the spec tree must specify both a view and its associated [NodeController]. + * Commands to add/remove/reorder children are sent to the controller. How the controller + * interprets these commands is left to its own discretion -- it might add them directly to its + * associated view or to some subview container. + * + * It's possible for nodes to mix "unmanaged" views in alongside managed ones within the same + * container. In this case, whenever the differ runs it will move all unmanaged views to the end + * of the node's child list. + */ +@MainThread +class ShadeViewDiffer( + rootController: NodeController, + private val logger: ShadeViewDifferLogger +) { + private val rootNode = ShadeNode(rootController) + private val nodes = mutableMapOf(rootController to rootNode) + private val views = mutableMapOf<View, ShadeNode>() + + /** + * Adds and removes views from the root (and its children) until their structure matches the + * provided [spec]. The root node of the spec must match the root controller passed to the + * differ's constructor. + */ + fun applySpec(spec: NodeSpec) { + val specMap = treeToMap(spec) + + if (spec.controller != rootNode.controller) { + throw IllegalArgumentException("Tree root ${spec.controller.nodeLabel} does not " + + "match own root at ${rootNode.label}") + } + + detachChildren(rootNode, specMap) + attachChildren(rootNode, specMap) + } + + /** + * If [view] is managed by this differ, then returns the label of the view's controller. + * Otherwise returns View.toString(). + * + * For debugging purposes. + */ + fun getViewLabel(view: View): String { + return views[view]?.label ?: view.toString() + } + + private fun detachChildren( + parentNode: ShadeNode, + specMap: Map<NodeController, NodeSpec> + ) { + val parentSpec = specMap[parentNode.controller] + + for (i in parentNode.getChildCount() - 1 downTo 0) { + val childView = parentNode.getChildAt(i) + views[childView]?.let { childNode -> + val childSpec = specMap[childNode.controller] + + maybeDetachChild(parentNode, parentSpec, childNode, childSpec) + + if (childNode.controller.getChildCount() > 0) { + detachChildren(childNode, specMap) + } + } + } + } + + private fun maybeDetachChild( + parentNode: ShadeNode, + parentSpec: NodeSpec?, + childNode: ShadeNode, + childSpec: NodeSpec? + ) { + val newParentNode = transform(childSpec?.parent) { getNode(it) } + + if (newParentNode != parentNode) { + val childCompletelyRemoved = newParentNode == null + + if (childCompletelyRemoved) { + nodes.remove(childNode.controller) + views.remove(childNode.controller.view) + } + + if (childCompletelyRemoved && parentSpec == null) { + // If both the child and the parent are being removed at the same time, then + // keep the child attached to the parent for animation purposes + logger.logSkippingDetach(childNode.label, parentNode.label) + } else { + logger.logDetachingChild( + childNode.label, + !childCompletelyRemoved, + parentNode.label, + newParentNode?.label) + parentNode.removeChild(childNode, !childCompletelyRemoved) + childNode.parent = null + } + } + } + + private fun attachChildren( + parentNode: ShadeNode, + specMap: Map<NodeController, NodeSpec> + ) { + val parentSpec = checkNotNull(specMap[parentNode.controller]) + + for ((index, childSpec) in parentSpec.children.withIndex()) { + val currView = parentNode.getChildAt(index) + val childNode = getNode(childSpec) + + if (childNode.view != currView) { + + when (childNode.parent) { + null -> { + // A new child (either newly created or coming from some other parent) + logger.logAttachingChild(childNode.label, parentNode.label) + parentNode.addChildAt(childNode, index) + childNode.parent = parentNode + } + parentNode -> { + // A pre-existing child, just in the wrong position. Move it into place + logger.logMovingChild(childNode.label, parentNode.label, index) + parentNode.moveChildTo(childNode, index) + } + else -> { + // Error: child still has a parent. We should have detached it in the + // previous step. + throw IllegalStateException("Child ${childNode.label} should have " + + "parent ${parentNode.label} but is actually " + + "${childNode.parent?.label}") + } + } + } + + if (childSpec.children.isNotEmpty()) { + attachChildren(childNode, specMap) + } + } + } + + private fun getNode(spec: NodeSpec): ShadeNode { + var node = nodes[spec.controller] + if (node == null) { + node = ShadeNode(spec.controller) + nodes[node.controller] = node + views[node.view] = node + } + return node + } + + private fun treeToMap(tree: NodeSpec): Map<NodeController, NodeSpec> { + val map = mutableMapOf<NodeController, NodeSpec>() + + registerNodes(tree, map) + + return map + } + + private fun registerNodes(node: NodeSpec, map: MutableMap<NodeController, NodeSpec>) { + if (map.containsKey(node.controller)) { + throw RuntimeException("Node ${node.controller.nodeLabel} appears more than once") + } + map[node.controller] = node + + if (node.children.isNotEmpty()) { + for (child in node.children) { + registerNodes(child, map) + } + } + } +} + +private class ShadeNode( + val controller: NodeController +) { + val view = controller.view + + var parent: ShadeNode? = null + + val label: String + get() = controller.nodeLabel + + fun getChildAt(index: Int): View? = controller.getChildAt(index) + + fun getChildCount(): Int = controller.getChildCount() + + fun addChildAt(child: ShadeNode, index: Int) { + controller.addChildAt(child.controller, index) + } + + fun moveChildTo(child: ShadeNode, index: Int) { + controller.moveChildTo(child.controller, index) + } + + fun removeChild(child: ShadeNode, isTransfer: Boolean) { + controller.removeChild(child.controller, isTransfer) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt index 3d561264d367..19e156f572d4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewManagerLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt @@ -21,7 +21,7 @@ import com.android.systemui.log.LogLevel import com.android.systemui.log.dagger.NotificationLog import javax.inject.Inject -class NotifViewManagerLogger @Inject constructor( +class ShadeViewDifferLogger @Inject constructor( @NotificationLog private val buffer: LogBuffer ) { fun logDetachingChild( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt new file mode 100644 index 000000000000..118ff4a9fbb7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.statusbar.notification.collection.render + +import com.android.systemui.statusbar.notification.collection.GroupEntry +import com.android.systemui.statusbar.notification.collection.ListEntry +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.ShadeListBuilder +import com.android.systemui.statusbar.notification.stack.NotificationListContainer +import com.android.systemui.statusbar.phone.NotificationIconAreaController +import java.lang.RuntimeException +import javax.inject.Inject + +/** + * Responsible for building and applying the "shade node spec": the list (tree) of things that + * currently populate the notification shade. + */ +class ShadeViewManager constructor( + listContainer: NotificationListContainer, + logger: ShadeViewDifferLogger, + private val viewBarn: NotifViewBarn, + private val notificationIconAreaController: NotificationIconAreaController +) { + private val rootController = RootNodeController(listContainer) + private val viewDiffer = ShadeViewDiffer(rootController, logger) + + fun attach(listBuilder: ShadeListBuilder) { + listBuilder.setOnRenderListListener(::onNewNotifTree) + } + + private fun onNewNotifTree(tree: List<ListEntry>) { + viewDiffer.applySpec(buildTree(tree)) + } + + private fun buildTree(notifList: List<ListEntry>): NodeSpec { + val root = NodeSpecImpl(null, rootController) + + for (entry in notifList) { + // TODO: Add section header logic here + root.children.add(buildNotifNode(entry, root)) + } + + notificationIconAreaController.updateNotificationIcons(notifList) + return root + } + + private fun buildNotifNode(entry: ListEntry, parent: NodeSpec): NodeSpec { + return when (entry) { + is NotificationEntry -> { + NodeSpecImpl(parent, viewBarn.requireView(entry)) + } + is GroupEntry -> { + val groupNode = NodeSpecImpl( + parent, + viewBarn.requireView(checkNotNull(entry.summary))) + + for (childEntry in entry.children) { + groupNode.children.add(buildNotifNode(childEntry, groupNode)) + } + + groupNode + } + else -> { + throw RuntimeException("Unexpected entry: $entry") + } + } + } +} + +class ShadeViewManagerFactory @Inject constructor( + private val logger: ShadeViewDifferLogger, + private val viewBarn: NotifViewBarn, + private val notificationIconAreaController: NotificationIconAreaController +) { + fun create(listContainer: NotificationListContainer): ShadeViewManager { + return ShadeViewManager(listContainer, logger, viewBarn, notificationIconAreaController) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 046dc3c8dce6..6d01324f1b7b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -28,9 +28,11 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; +import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.CurrentUserContextTracker; import com.android.systemui.statusbar.FeatureFlags; @@ -40,14 +42,16 @@ import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; +import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator; import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; -import com.android.systemui.statusbar.notification.collection.inflation.OnDismissCallbackImpl; +import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl; +import com.android.systemui.statusbar.notification.collection.legacy.OnUserInteractionCallbackImplLegacy; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.init.NotificationsController; @@ -61,7 +65,7 @@ import com.android.systemui.statusbar.notification.logging.NotificationPanelLogg import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; -import com.android.systemui.statusbar.notification.row.OnDismissCallback; +import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.StatusBar; @@ -71,7 +75,6 @@ import com.android.systemui.util.leak.LeakDetector; import java.util.concurrent.Executor; import javax.inject.Provider; -import javax.inject.Singleton; import dagger.Binds; import dagger.Lazy; @@ -84,7 +87,7 @@ import dagger.Provides; @Module public interface NotificationsModule { /** Provides an instance of {@link NotificationEntryManager} */ - @Singleton + @SysUISingleton @Provides static NotificationEntryManager provideNotificationEntryManager( NotificationEntryManagerLogger logger, @@ -111,11 +114,10 @@ public interface NotificationsModule { } /** Provides an instance of {@link NotificationGutsManager} */ - @Singleton + @SysUISingleton @Provides static NotificationGutsManager provideNotificationGutsManager( Context context, - VisualStabilityManager visualStabilityManager, Lazy<StatusBar> statusBarLazy, @Main Handler mainHandler, @Background Handler bgHandler, @@ -129,10 +131,10 @@ public interface NotificationsModule { Provider<PriorityOnboardingDialogController.Builder> builderProvider, AssistantFeedbackController assistantFeedbackController, BubbleController bubbleController, - UiEventLogger uiEventLogger) { + UiEventLogger uiEventLogger, + OnUserInteractionCallback onUserInteractionCallback) { return new NotificationGutsManager( context, - visualStabilityManager, statusBarLazy, mainHandler, bgHandler, @@ -146,19 +148,28 @@ public interface NotificationsModule { builderProvider, assistantFeedbackController, bubbleController, - uiEventLogger); + uiEventLogger, + onUserInteractionCallback); } /** Provides an instance of {@link VisualStabilityManager} */ - @Singleton + @SysUISingleton @Provides static VisualStabilityManager provideVisualStabilityManager( - NotificationEntryManager notificationEntryManager, Handler handler) { - return new VisualStabilityManager(notificationEntryManager, handler); + FeatureFlags featureFlags, + NotificationEntryManager notificationEntryManager, + Handler handler, + StatusBarStateController statusBarStateController, + WakefulnessLifecycle wakefulnessLifecycle) { + return new VisualStabilityManager( + notificationEntryManager, + handler, + statusBarStateController, + wakefulnessLifecycle); } /** Provides an instance of {@link NotificationLogger} */ - @Singleton + @SysUISingleton @Provides static NotificationLogger provideNotificationLogger( NotificationListener notificationListener, @@ -177,14 +188,14 @@ public interface NotificationsModule { } /** Provides an instance of {@link NotificationPanelLogger} */ - @Singleton + @SysUISingleton @Provides static NotificationPanelLogger provideNotificationPanelLogger() { return new NotificationPanelLoggerImpl(); } /** Provides an instance of {@link NotificationBlockingHelperManager} */ - @Singleton + @SysUISingleton @Provides static NotificationBlockingHelperManager provideNotificationBlockingHelperManager( Context context, @@ -196,7 +207,7 @@ public interface NotificationsModule { } /** Initializes the notification data pipeline (can be disabled via config). */ - @Singleton + @SysUISingleton @Provides static NotificationsController provideNotificationsController( Context context, @@ -213,7 +224,7 @@ public interface NotificationsModule { * Provide the active notification collection managing the notifications to render. */ @Provides - @Singleton + @SysUISingleton static CommonNotifCollection provideCommonNotifCollection( FeatureFlags featureFlags, Lazy<NotifPipeline> pipeline, @@ -226,21 +237,28 @@ public interface NotificationsModule { * from the notification shade or it gets auto-cancelled by click. */ @Provides - @Singleton - static OnDismissCallback provideOnDismissCallback( + @SysUISingleton + static OnUserInteractionCallback provideOnUserInteractionCallback( FeatureFlags featureFlags, HeadsUpManager headsUpManager, StatusBarStateController statusBarStateController, Lazy<NotifPipeline> pipeline, Lazy<NotifCollection> notifCollection, - NotificationEntryManager entryManager) { + Lazy<VisualStabilityCoordinator> visualStabilityCoordinator, + NotificationEntryManager entryManager, + VisualStabilityManager visualStabilityManager) { return featureFlags.isNewNotifPipelineRenderingEnabled() - ? new OnDismissCallbackImpl( - pipeline.get(), notifCollection.get(), headsUpManager, - statusBarStateController) - : new com.android.systemui.statusbar.notification.collection - .legacy.OnDismissCallbackImpl( - entryManager, headsUpManager, statusBarStateController); + ? new OnUserInteractionCallbackImpl( + pipeline.get(), + notifCollection.get(), + headsUpManager, + statusBarStateController, + visualStabilityCoordinator.get()) + : new OnUserInteractionCallbackImplLegacy( + entryManager, + headsUpManager, + statusBarStateController, + visualStabilityManager); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt index 6e4fcd5f97b1..6460892952e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.init import android.service.notification.StatusBarNotification +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption import com.android.systemui.statusbar.FeatureFlags import com.android.systemui.statusbar.NotificationListener @@ -26,10 +27,11 @@ import com.android.systemui.statusbar.notification.NotificationClicker import com.android.systemui.statusbar.notification.NotificationEntryManager import com.android.systemui.statusbar.notification.NotificationListController import com.android.systemui.statusbar.notification.collection.NotifPipeline +import com.android.systemui.statusbar.notification.collection.TargetSdkResolver import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer -import com.android.systemui.statusbar.notification.collection.TargetSdkResolver import com.android.systemui.statusbar.notification.interruption.HeadsUpController +import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder import com.android.systemui.statusbar.notification.row.NotifBindPipelineInitializer import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper @@ -37,14 +39,12 @@ import com.android.systemui.statusbar.phone.NotificationGroupManager import com.android.systemui.statusbar.phone.StatusBar import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.HeadsUpManager -import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder import com.android.systemui.statusbar.policy.RemoteInputUriController import dagger.Lazy import java.io.FileDescriptor import java.io.PrintWriter import java.util.Optional import javax.inject.Inject -import javax.inject.Singleton /** * Master controller for all notifications-related work @@ -53,7 +53,7 @@ import javax.inject.Singleton * Once we migrate away from the need for such things, this class becomes primarily a place to do * any initialization work that notifications require. */ -@Singleton +@SysUISingleton class NotificationsControllerImpl @Inject constructor( private val featureFlags: FeatureFlags, private val notificationListener: NotificationListener, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt index 0fd865b603f8..f8d6c6d8ec10 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt @@ -20,6 +20,7 @@ import android.content.Context import android.media.MediaMetadata import android.provider.Settings import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationMediaManager @@ -30,13 +31,12 @@ import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.tuner.TunerService import javax.inject.Inject -import javax.inject.Singleton /** * A class that automatically creates heads up for important notification when bypassing the * lockscreen */ -@Singleton +@SysUISingleton class BypassHeadsUpNotifier @Inject constructor( private val context: Context, private val bypassController: KeyguardBypassController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java index 9b6ae9a7f99d..4d56e6013d71 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java @@ -24,25 +24,25 @@ import android.util.Log; import androidx.annotation.NonNull; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controller class for old pipeline heads up logic. It listens to {@link NotificationEntryManager} * entry events and appropriately binds or unbinds the heads up view and promotes it to the top * of the screen. */ -@Singleton +@SysUISingleton public class HeadsUpController { private final HeadsUpViewBinder mHeadsUpViewBinder; private final NotificationInterruptStateProvider mInterruptStateProvider; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java index ff139957031a..ffec3671995f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java @@ -24,6 +24,7 @@ import androidx.annotation.Nullable; import androidx.core.os.CancellationSignal; import com.android.internal.util.NotificationMessagingUtil; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator; @@ -34,7 +35,6 @@ import com.android.systemui.statusbar.notification.row.RowContentBindStage; import java.util.Map; import javax.inject.Inject; -import javax.inject.Singleton; /** * Wrapper around heads up view binding logic. {@link HeadsUpViewBinder} is responsible for @@ -44,7 +44,7 @@ import javax.inject.Singleton; * TODO: This should be moved into {@link HeadsUpCoordinator} when the old pipeline is deprecated * (i.e. when {@link HeadsUpController} is removed). */ -@Singleton +@SysUISingleton public class HeadsUpViewBinder { private final RowContentBindStage mStage; private final NotificationMessagingUtil mNotificationMessagingUtil; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index 71f6dac4e234..433d5e19ed6b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -32,6 +32,7 @@ import android.service.notification.StatusBarNotification; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; @@ -44,12 +45,11 @@ import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * Provides heads-up and pulsing state for notification entries. */ -@Singleton +@SysUISingleton public class NotificationInterruptStateProviderImpl implements NotificationInterruptStateProvider { private static final String TAG = "InterruptionStateProvider"; private static final boolean DEBUG = true; //false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt index d32d09dff630..743bf332fc9d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt @@ -35,6 +35,7 @@ import com.android.internal.statusbar.NotificationVisibility import com.android.internal.widget.MessagingGroup import com.android.settingslib.notification.ConversationIconFactory import com.android.systemui.R +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.NotificationPersonExtractorPlugin @@ -48,7 +49,6 @@ import com.android.systemui.statusbar.policy.ExtensionController import java.util.ArrayDeque import java.util.concurrent.Executor import javax.inject.Inject -import javax.inject.Singleton private const val MAX_STORED_INACTIVE_PEOPLE = 10 @@ -58,7 +58,7 @@ interface NotificationPersonExtractor { fun isPersonNotification(sbn: StatusBarNotification): Boolean } -@Singleton +@SysUISingleton class NotificationPersonExtractorPluginBoundary @Inject constructor( extensionController: ExtensionController ) : NotificationPersonExtractor { @@ -87,7 +87,7 @@ class NotificationPersonExtractorPluginBoundary @Inject constructor( plugin?.isPersonNotification(sbn) ?: false } -@Singleton +@SysUISingleton class PeopleHubDataSourceImpl @Inject constructor( private val notificationEntryManager: NotificationEntryManager, private val extractor: NotificationPersonExtractor, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt index 7f42fe0473f3..55bd77fdab68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt @@ -23,10 +23,10 @@ import android.os.Handler import android.os.UserHandle import android.provider.Settings import android.view.View +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter import javax.inject.Inject -import javax.inject.Singleton /** Boundary between the View and PeopleHub, as seen by the View. */ interface PeopleHubViewAdapter { @@ -68,7 +68,7 @@ interface PeopleHubViewModelFactory { * * @param dataSource PeopleHub data pipeline. */ -@Singleton +@SysUISingleton class PeopleHubViewAdapterImpl @Inject constructor( private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubViewModelFactory> ) : PeopleHubViewAdapter { @@ -99,7 +99,7 @@ private class PeopleHubDataListenerImpl( * This class serves as the glue between the View layer (which depends on * [PeopleHubViewBoundary]) and the Data layer (which produces [PeopleHubModel]s). */ -@Singleton +@SysUISingleton class PeopleHubViewModelFactoryDataSourceImpl @Inject constructor( private val activityStarter: ActivityStarter, private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubModel> @@ -151,7 +151,7 @@ private class PeopleHubViewModelFactoryImpl( } } -@Singleton +@SysUISingleton class PeopleHubSettingChangeDataSourceImpl @Inject constructor( @Main private val handler: Handler, context: Context diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt index d36627fb849d..1ac2cb5a36d5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.people import android.annotation.IntDef import android.service.notification.NotificationListenerService.Ranking import android.service.notification.StatusBarNotification +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON @@ -26,7 +27,6 @@ import com.android.systemui.statusbar.notification.people.PeopleNotificationIden import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON import com.android.systemui.statusbar.phone.NotificationGroupManager import javax.inject.Inject -import javax.inject.Singleton import kotlin.math.max interface PeopleNotificationIdentifier { @@ -59,7 +59,7 @@ interface PeopleNotificationIdentifier { } } -@Singleton +@SysUISingleton class PeopleNotificationIdentifierImpl @Inject constructor( private val personExtractor: NotificationPersonExtractor, private val groupManager: NotificationGroupManager diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java deleted file mode 100644 index 28c53dc6d9b2..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.systemui.statusbar.notification.row; - -import android.app.AppOpsManager; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.graphics.drawable.Drawable; -import android.service.notification.StatusBarNotification; -import android.util.ArraySet; -import android.util.AttributeSet; -import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.UiEventLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.systemui.R; - -/** - * The guts of a notification revealed when performing a long press. - */ -public class AppOpsInfo extends LinearLayout implements NotificationGuts.GutsContent { - private static final String TAG = "AppOpsGuts"; - - private PackageManager mPm; - - private String mPkg; - private String mAppName; - private int mAppUid; - private StatusBarNotification mSbn; - private ArraySet<Integer> mAppOps; - private MetricsLogger mMetricsLogger; - private OnSettingsClickListener mOnSettingsClickListener; - private NotificationGuts mGutsContainer; - private UiEventLogger mUiEventLogger; - - private OnClickListener mOnOk = v -> { - mGutsContainer.closeControls(v, false); - }; - - public AppOpsInfo(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public interface OnSettingsClickListener { - void onClick(View v, String pkg, int uid, ArraySet<Integer> ops); - } - - public void bindGuts(final PackageManager pm, - final OnSettingsClickListener onSettingsClick, - final StatusBarNotification sbn, - final UiEventLogger uiEventLogger, - ArraySet<Integer> activeOps) { - mPkg = sbn.getPackageName(); - mSbn = sbn; - mPm = pm; - mAppName = mPkg; - mOnSettingsClickListener = onSettingsClick; - mAppOps = activeOps; - mUiEventLogger = uiEventLogger; - - bindHeader(); - bindPrompt(); - bindButtons(); - - logUiEvent(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_OPEN); - mMetricsLogger = new MetricsLogger(); - mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, true); - } - - private void bindHeader() { - // Package name - Drawable pkgicon = null; - ApplicationInfo info; - try { - info = mPm.getApplicationInfo(mPkg, - PackageManager.MATCH_UNINSTALLED_PACKAGES - | PackageManager.MATCH_DISABLED_COMPONENTS - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.MATCH_DIRECT_BOOT_AWARE); - if (info != null) { - mAppUid = mSbn.getUid(); - mAppName = String.valueOf(mPm.getApplicationLabel(info)); - pkgicon = mPm.getApplicationIcon(info); - } - } catch (PackageManager.NameNotFoundException e) { - // app is gone, just show package name and generic icon - pkgicon = mPm.getDefaultActivityIcon(); - } - ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon); - ((TextView) findViewById(R.id.pkgname)).setText(mAppName); - } - - private void bindPrompt() { - final TextView prompt = findViewById(R.id.prompt); - prompt.setText(getPrompt()); - } - - private void bindButtons() { - View settings = findViewById(R.id.settings); - settings.setOnClickListener((View view) -> { - mOnSettingsClickListener.onClick(view, mPkg, mAppUid, mAppOps); - }); - TextView ok = findViewById(R.id.ok); - ok.setOnClickListener(mOnOk); - ok.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate()); - } - - private String getPrompt() { - if (mAppOps == null || mAppOps.size() == 0) { - return ""; - } else if (mAppOps.size() == 1) { - if (mAppOps.contains(AppOpsManager.OP_CAMERA)) { - return mContext.getString(R.string.appops_camera); - } else if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) { - return mContext.getString(R.string.appops_microphone); - } else { - return mContext.getString(R.string.appops_overlay); - } - } else if (mAppOps.size() == 2) { - if (mAppOps.contains(AppOpsManager.OP_CAMERA)) { - if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) { - return mContext.getString(R.string.appops_camera_mic); - } else { - return mContext.getString(R.string.appops_camera_overlay); - } - } else { - return mContext.getString(R.string.appops_mic_overlay); - } - } else { - return mContext.getString(R.string.appops_camera_mic_overlay); - } - } - - @Override - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { - super.onInitializeAccessibilityEvent(event); - if (mGutsContainer != null && - event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { - if (mGutsContainer.isExposed()) { - event.getText().add(mContext.getString( - R.string.notification_channel_controls_opened_accessibility, mAppName)); - } else { - event.getText().add(mContext.getString( - R.string.notification_channel_controls_closed_accessibility, mAppName)); - } - } - } - - @Override - public void setGutsParent(NotificationGuts guts) { - mGutsContainer = guts; - } - - @Override - public boolean willBeRemoved() { - return false; - } - - @Override - public boolean shouldBeSaved() { - return false; - } - - @Override - public boolean needsFalsingProtection() { - return false; - } - - @Override - public View getContentView() { - return this; - } - - @Override - public boolean handleCloseControls(boolean save, boolean force) { - logUiEvent(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_CLOSE); - if (mMetricsLogger != null) { - mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, false); - } - return false; - } - - @Override - public int getActualHeight() { - return getHeight(); - } - - private void logUiEvent(NotificationAppOpsEvent event) { - if (mSbn != null) { - mUiEventLogger.logWithInstanceId(event, - mSbn.getUid(), mSbn.getPackageName(), mSbn.getInstanceId()); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt index 93db9cdf85ef..9bba7effd824 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt @@ -37,12 +37,10 @@ import android.view.Window import android.view.WindowInsets.Type.statusBars import android.view.WindowManager import android.widget.TextView - import com.android.internal.annotations.VisibleForTesting import com.android.systemui.R - +import com.android.systemui.dagger.SysUISingleton import javax.inject.Inject -import javax.inject.Singleton private const val TAG = "ChannelDialogController" @@ -58,7 +56,7 @@ private const val TAG = "ChannelDialogController" * - the next 3 channels sorted alphabetically for that app <on/off> * - <on/off> */ -@Singleton +@SysUISingleton class ChannelEditorDialogController @Inject constructor( c: Context, private val noMan: INotificationManager, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 22d0357b14c2..9c09cba5e137 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -84,8 +84,8 @@ import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.AboveShelfChangedListener; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationUtils; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.logging.NotificationCounters; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; @@ -239,7 +239,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private boolean mShowNoBackground; private ExpandableNotificationRow mNotificationParent; private OnExpandClickListener mOnExpandClickListener; - private View.OnClickListener mOnAppOpsClickListener; + private View.OnClickListener mOnAppClickListener; private View.OnClickListener mOnFeedbackClickListener; // Listener will be called when receiving a long click event. @@ -323,7 +323,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private View mGroupParentWhenDismissed; private boolean mShelfIconVisible; private boolean mAboveShelf; - private OnDismissCallback mOnDismissCallback; + private OnUserInteractionCallback mOnUserInteractionCallback; private boolean mIsLowPriority; private boolean mIsColorized; private boolean mUseIncreasedCollapsedHeight; @@ -1139,7 +1139,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView items.add(NotificationMenuRow.createPartialConversationItem(mContext)); items.add(NotificationMenuRow.createInfoItem(mContext)); items.add(NotificationMenuRow.createSnoozeItem(mContext)); - items.add(NotificationMenuRow.createAppOpsItem(mContext)); mMenuRow.setMenuItems(items); } if (existed) { @@ -1446,8 +1445,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } dismiss(fromAccessibility); if (mEntry.isClearable()) { - if (mOnDismissCallback != null) { - mOnDismissCallback.onDismiss(mEntry, REASON_CANCEL); + if (mOnUserInteractionCallback != null) { + mOnUserInteractionCallback.onDismiss(mEntry, REASON_CANCEL); } } } @@ -1464,10 +1463,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mIsBlockingHelperShowing && mNotificationTranslationFinished; } - void setOnDismissCallback(OnDismissCallback onDismissCallback) { - mOnDismissCallback = onDismissCallback; - } - @Override public View getShelfTransformationTarget() { if (mIsSummaryWithChildren && !shouldShowPublic()) { @@ -1598,11 +1593,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView RowContentBindStage rowContentBindStage, OnExpandClickListener onExpandClickListener, NotificationMediaManager notificationMediaManager, - CoordinateOnClickListener onAppOpsClickListener, CoordinateOnClickListener onFeedbackClickListener, FalsingManager falsingManager, StatusBarStateController statusBarStateController, - PeopleNotificationIdentifier peopleNotificationIdentifier) { + PeopleNotificationIdentifier peopleNotificationIdentifier, + OnUserInteractionCallback onUserInteractionCallback) { mAppName = appName; if (mMenuRow == null) { mMenuRow = new NotificationMenuRow(mContext, peopleNotificationIdentifier); @@ -1619,7 +1614,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mRowContentBindStage = rowContentBindStage; mOnExpandClickListener = onExpandClickListener; mMediaManager = notificationMediaManager; - setAppOpsOnClickListener(onAppOpsClickListener); setOnFeedbackClickListener(onFeedbackClickListener); mFalsingManager = falsingManager; mStatusbarStateController = statusBarStateController; @@ -1627,6 +1621,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView for (NotificationContentView l : mLayouts) { l.setPeopleNotificationIdentifier(mPeopleNotificationIdentifier); } + mOnUserInteractionCallback = onUserInteractionCallback; } private void initDimens() { @@ -1677,14 +1672,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView requestLayout(); } - public void showAppOpsIcons(ArraySet<Integer> activeOps) { - if (mIsSummaryWithChildren) { - mChildrenContainer.showAppOpsIcons(activeOps); - } - mPrivateLayout.showAppOpsIcons(activeOps); - mPublicLayout.showAppOpsIcons(activeOps); - } - public void showFeedbackIcon(boolean show) { if (mIsSummaryWithChildren) { mChildrenContainer.showFeedbackIcon(show); @@ -1721,24 +1708,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mPublicLayout.setRecentlyAudiblyAlerted(audiblyAlertedRecently); } - public View.OnClickListener getAppOpsOnClickListener() { - return mOnAppOpsClickListener; - } - - void setAppOpsOnClickListener(CoordinateOnClickListener l) { - mOnAppOpsClickListener = v -> { - createMenu(); - NotificationMenuRowPlugin provider = getProvider(); - if (provider == null) { - return; - } - MenuItem menuItem = provider.getAppOpsMenuItem(mContext); - if (menuItem != null) { - l.onClick(this, v.getWidth() / 2, v.getHeight() / 2, menuItem); - } - }; - } - public View.OnClickListener getFeedbackOnClickListener() { return mOnFeedbackClickListener; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index 86a3271c4eac..f8bc2bebf93b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -22,21 +22,27 @@ import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENAB import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; + import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.notification.collection.render.NodeController; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.dagger.AppName; import com.android.systemui.statusbar.notification.row.dagger.NotificationKey; import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope; +import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.time.SystemClock; +import java.util.List; + import javax.inject.Inject; import javax.inject.Named; @@ -44,8 +50,9 @@ import javax.inject.Named; * Controller for {@link ExpandableNotificationRow}. */ @NotificationRowScope -public class ExpandableNotificationRowController { +public class ExpandableNotificationRowController implements NodeController { private final ExpandableNotificationRow mView; + private final NotificationListContainer mListContainer; private final ActivatableNotificationViewController mActivatableNotificationViewController; private final NotificationMediaManager mMediaManager; private final PluginManager mPluginManager; @@ -62,16 +69,16 @@ public class ExpandableNotificationRowController { private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger = this::logNotificationExpansion; - private final ExpandableNotificationRow.CoordinateOnClickListener mOnAppOpsClickListener; private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener; private final NotificationGutsManager mNotificationGutsManager; - private final OnDismissCallback mOnDismissCallback; + private final OnUserInteractionCallback mOnUserInteractionCallback; private final FalsingManager mFalsingManager; private final boolean mAllowLongPress; private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; @Inject public ExpandableNotificationRowController(ExpandableNotificationRow view, + NotificationListContainer listContainer, ActivatableNotificationViewController activatableNotificationViewController, NotificationMediaManager mediaManager, PluginManager pluginManager, SystemClock clock, @AppName String appName, @NotificationKey String notificationKey, @@ -83,9 +90,10 @@ public class ExpandableNotificationRowController { StatusBarStateController statusBarStateController, NotificationGutsManager notificationGutsManager, @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, - OnDismissCallback onDismissCallback, FalsingManager falsingManager, + OnUserInteractionCallback onUserInteractionCallback, FalsingManager falsingManager, PeopleNotificationIdentifier peopleNotificationIdentifier) { mView = view; + mListContainer = listContainer; mActivatableNotificationViewController = activatableNotificationViewController; mMediaManager = mediaManager; mPluginManager = pluginManager; @@ -100,8 +108,7 @@ public class ExpandableNotificationRowController { mOnExpandClickListener = onExpandClickListener; mStatusBarStateController = statusBarStateController; mNotificationGutsManager = notificationGutsManager; - mOnDismissCallback = onDismissCallback; - mOnAppOpsClickListener = mNotificationGutsManager::openGuts; + mOnUserInteractionCallback = onUserInteractionCallback; mOnFeedbackClickListener = mNotificationGutsManager::openGuts; mAllowLongPress = allowLongPress; mFalsingManager = falsingManager; @@ -123,13 +130,12 @@ public class ExpandableNotificationRowController { mRowContentBindStage, mOnExpandClickListener, mMediaManager, - mOnAppOpsClickListener, mOnFeedbackClickListener, mFalsingManager, mStatusBarStateController, - mPeopleNotificationIdentifier + mPeopleNotificationIdentifier, + mOnUserInteractionCallback ); - mView.setOnDismissCallback(mOnDismissCallback); mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); if (mAllowLongPress) { mView.setLongPressListener((v, x, y, item) -> { @@ -162,4 +168,52 @@ public class ExpandableNotificationRowController { private void logNotificationExpansion(String key, boolean userAction, boolean expanded) { mNotificationLogger.onExpansionChanged(key, userAction, expanded); } + + @Override + @NonNull + public String getNodeLabel() { + return mView.getEntry().getKey(); + } + + @Override + @NonNull + public View getView() { + return mView; + } + + @Override + public View getChildAt(int index) { + return mView.getChildNotificationAt(index); + } + + @Override + public void addChildAt(NodeController child, int index) { + ExpandableNotificationRow childView = (ExpandableNotificationRow) child.getView(); + + mView.addChildNotification((ExpandableNotificationRow) child.getView()); + mListContainer.notifyGroupChildAdded(childView); + } + + @Override + public void moveChildTo(NodeController child, int index) { + ExpandableNotificationRow childView = (ExpandableNotificationRow) child.getView(); + mView.removeChildNotification(childView); + mView.addChildNotification(childView, index); + } + + @Override + public void removeChild(NodeController child, boolean isTransfer) { + ExpandableNotificationRow childView = (ExpandableNotificationRow) child.getView(); + + mView.removeChildNotification(childView); + if (!isTransfer) { + mListContainer.notifyGroupChildRemoved(childView, mView); + } + } + + @Override + public int getChildCount() { + final List<ExpandableNotificationRow> mChildren = mView.getAttachedChildren(); + return mChildren != null ? mChildren.size() : 0; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java index 90d30dcf38ab..f693ebbb7830 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java @@ -28,6 +28,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.os.CancellationSignal; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; @@ -41,7 +42,6 @@ import java.util.Map; import java.util.Set; import javax.inject.Inject; -import javax.inject.Singleton; /** * {@link NotifBindPipeline} is responsible for converting notifications from their data form to @@ -77,7 +77,7 @@ import javax.inject.Singleton; * views and assumes that a row is given to it when it's inflated. */ @MainThread -@Singleton +@SysUISingleton public final class NotifBindPipeline { private final Map<NotificationEntry, BindEntry> mBindEntries = new ArrayMap<>(); private final NotifBindPipelineLogger mLogger; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManager.java index a3ca08432745..51eb9f7220fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManager.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.row; import androidx.annotation.NonNull; import androidx.collection.ArraySet; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.util.ArrayList; @@ -26,14 +27,13 @@ import java.util.List; import java.util.Set; import javax.inject.Inject; -import javax.inject.Singleton; /** * A manager handling the error state of a notification when it encounters an exception while * inflating. We don't want to show these notifications to the user but may want to keep them * around for logging purposes. */ -@Singleton +@SysUISingleton public class NotifInflationErrorManager { Set<NotificationEntry> mErroredNotifs = new ArraySet<>(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index a7d83b3b2774..9bcac1163acc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -36,6 +36,7 @@ import android.widget.RemoteViews; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.ImageMessageConsumer; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.media.MediaDataManagerKt; import com.android.systemui.media.MediaFeatureFlag; @@ -58,7 +59,6 @@ import java.util.HashMap; import java.util.concurrent.Executor; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; @@ -66,7 +66,7 @@ import dagger.Lazy; * {@link NotificationContentInflater} binds content to a {@link ExpandableNotificationRow} by * asynchronously building the content's {@link RemoteViews} and applying it to the row. */ -@Singleton +@SysUISingleton @VisibleForTesting(visibility = PACKAGE) public class NotificationContentInflater implements NotificationRowContentBinder { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 2986b9b75b98..c7e44c5e8e0a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -1568,18 +1568,6 @@ public class NotificationContentView extends FrameLayout { return header; } - public void showAppOpsIcons(ArraySet<Integer> activeOps) { - if (mContractedChild != null) { - mContractedWrapper.showAppOpsIcons(activeOps); - } - if (mExpandedChild != null) { - mExpandedWrapper.showAppOpsIcons(activeOps); - } - if (mHeadsUpChild != null) { - mHeadsUpWrapper.showAppOpsIcons(activeOps); - } - } - public void showFeedbackIcon(boolean show) { if (mContractedChild != null) { mContractedWrapper.showFeedbackIcon(show); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index f543db74d91a..7c7bb5c83762 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -69,7 +69,6 @@ import com.android.systemui.bubbles.BubbleController; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.notification.NotificationChannelHelper; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; @@ -89,7 +88,7 @@ public class NotificationConversationInfo extends LinearLayout implements private ShortcutManager mShortcutManager; private PackageManager mPm; private ConversationIconFactory mIconFactory; - private VisualStabilityManager mVisualStabilityManager; + private OnUserInteractionCallback mOnUserInteractionCallback; private Handler mMainHandler; private Handler mBgHandler; private BubbleController mBubbleController; @@ -207,7 +206,7 @@ public class NotificationConversationInfo extends LinearLayout implements ShortcutManager shortcutManager, PackageManager pm, INotificationManager iNotificationManager, - VisualStabilityManager visualStabilityManager, + OnUserInteractionCallback onUserInteractionCallback, String pkg, NotificationChannel notificationChannel, NotificationEntry entry, @@ -224,7 +223,7 @@ public class NotificationConversationInfo extends LinearLayout implements BubbleController bubbleController) { mSelectedAction = -1; mINotificationManager = iNotificationManager; - mVisualStabilityManager = visualStabilityManager; + mOnUserInteractionCallback = onUserInteractionCallback; mPackageName = pkg; mEntry = entry; mSbn = entry.getSbn(); @@ -513,7 +512,7 @@ public class NotificationConversationInfo extends LinearLayout implements mAppUid, mSelectedAction, mNotificationChannel)); mEntry.markForUserTriggeredMovement(true); mMainHandler.postDelayed( - mVisualStabilityManager::temporarilyAllowReordering, + () -> mOnUserInteractionCallback.onImportanceChanged(mEntry), StackStateAnimator.ANIMATION_DURATION_STANDARD); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 729b131ac553..60074f608969 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -60,7 +60,6 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.dagger.NotificationsModule; @@ -88,10 +87,10 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); private final Context mContext; - private final VisualStabilityManager mVisualStabilityManager; private final AccessibilityManager mAccessibilityManager; private final HighPriorityProvider mHighPriorityProvider; private final ChannelEditorDialogController mChannelEditorDialogController; + private final OnUserInteractionCallback mOnUserInteractionCallback; // Dependencies: private final NotificationLockscreenUserManager mLockscreenUserManager = @@ -129,8 +128,10 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx /** * Injected constructor. See {@link NotificationsModule}. */ - public NotificationGutsManager(Context context, VisualStabilityManager visualStabilityManager, - Lazy<StatusBar> statusBarLazy, @Main Handler mainHandler, @Background Handler bgHandler, + public NotificationGutsManager(Context context, + Lazy<StatusBar> statusBarLazy, + @Main Handler mainHandler, + @Background Handler bgHandler, AccessibilityManager accessibilityManager, HighPriorityProvider highPriorityProvider, INotificationManager notificationManager, @@ -141,9 +142,9 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx Provider<PriorityOnboardingDialogController.Builder> builderProvider, AssistantFeedbackController assistantFeedbackController, BubbleController bubbleController, - UiEventLogger uiEventLogger) { + UiEventLogger uiEventLogger, + OnUserInteractionCallback onUserInteractionCallback) { mContext = context; - mVisualStabilityManager = visualStabilityManager; mStatusBarLazy = statusBarLazy; mMainHandler = mainHandler; mBgHandler = bgHandler; @@ -158,6 +159,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mAssistantFeedbackController = assistantFeedbackController; mBubbleController = bubbleController; mUiEventLogger = uiEventLogger; + mOnUserInteractionCallback = onUserInteractionCallback; } public void setUpWithPresenter(NotificationPresenter presenter, @@ -268,8 +270,6 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx try { if (gutsView instanceof NotificationSnooze) { initializeSnoozeView(row, (NotificationSnooze) gutsView); - } else if (gutsView instanceof AppOpsInfo) { - initializeAppOpsInfo(row, (AppOpsInfo) gutsView); } else if (gutsView instanceof NotificationInfo) { initializeNotificationInfo(row, (NotificationInfo) gutsView); } else if (gutsView instanceof NotificationConversationInfo) { @@ -309,36 +309,6 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx } /** - * Sets up the {@link AppOpsInfo} inside the notification row's guts. - * - * @param row view to set up the guts for - * @param appOpsInfoView view to set up/bind within {@code row} - */ - private void initializeAppOpsInfo( - final ExpandableNotificationRow row, - AppOpsInfo appOpsInfoView) { - NotificationGuts guts = row.getGuts(); - StatusBarNotification sbn = row.getEntry().getSbn(); - UserHandle userHandle = sbn.getUser(); - PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext, - userHandle.getIdentifier()); - - AppOpsInfo.OnSettingsClickListener onSettingsClick = - (View v, String pkg, int uid, ArraySet<Integer> ops) -> { - mUiEventLogger.logWithInstanceId( - NotificationAppOpsEvent.NOTIFICATION_APP_OPS_SETTINGS_CLICK, - sbn.getUid(), sbn.getPackageName(), sbn.getInstanceId()); - mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS); - guts.resetFalsingCheck(); - startAppOpsSettingsActivity(pkg, uid, ops, row); - }; - if (!row.getEntry().mActiveAppOps.isEmpty()) { - appOpsInfoView.bindGuts(pmUser, onSettingsClick, sbn, mUiEventLogger, - row.getEntry().mActiveAppOps); - } - } - - /** * Sets up the {@link FeedbackInfo} inside the notification row's guts. * * @param row view to set up the guts for @@ -395,7 +365,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx notificationInfoView.bindNotification( pmUser, mNotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, packageName, row.getEntry().getChannel(), @@ -506,7 +476,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mShortcutManager, pmUser, mNotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, packageName, entry.getChannel(), entry, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index a6ba85fc8bdf..7a976ac82223 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -60,7 +60,6 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.lang.annotation.Retention; @@ -93,9 +92,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private TextView mAutomaticDescriptionView; private INotificationManager mINotificationManager; + private OnUserInteractionCallback mOnUserInteractionCallback; private PackageManager mPm; private MetricsLogger mMetricsLogger; - private VisualStabilityManager mVisualStabilityManager; private ChannelEditorDialogController mChannelEditorDialogController; private String mPackageName; @@ -119,6 +118,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private boolean mIsAutomaticChosen; private boolean mIsSingleDefaultChannel; private boolean mIsNonblockable; + private NotificationEntry mEntry; private StatusBarNotification mSbn; private boolean mIsDeviceProvisioned; @@ -188,7 +188,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G public void bindNotification( PackageManager pm, INotificationManager iNotificationManager, - VisualStabilityManager visualStabilityManager, + OnUserInteractionCallback onUserInteractionCallback, ChannelEditorDialogController channelEditorDialogController, String pkg, NotificationChannel notificationChannel, @@ -204,11 +204,12 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G throws RemoteException { mINotificationManager = iNotificationManager; mMetricsLogger = Dependency.get(MetricsLogger.class); - mVisualStabilityManager = visualStabilityManager; + mOnUserInteractionCallback = onUserInteractionCallback; mChannelEditorDialogController = channelEditorDialogController; mPackageName = pkg; mUniqueChannelsInRow = uniqueChannelsInRow; mNumUniqueChannelsInRow = uniqueChannelsInRow.size(); + mEntry = entry; mSbn = entry.getSbn(); mPm = pm; mAppSettingsClickListener = onAppSettingsClick; @@ -438,7 +439,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid, mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null, mStartingChannelImportance, newImportance, mIsAutomaticChosen)); - mVisualStabilityManager.temporarilyAllowReordering(); + mOnUserInteractionCallback.onImportanceChanged(mEntry); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java index d264af94947d..205cecc92e55 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java @@ -76,7 +76,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl private Context mContext; private FrameLayout mMenuContainer; private NotificationMenuItem mInfoItem; - private MenuItem mAppOpsItem; private MenuItem mFeedbackItem; private MenuItem mSnoozeItem; private ArrayList<MenuItem> mLeftMenuItems; @@ -138,11 +137,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl } @Override - public MenuItem getAppOpsMenuItem(Context context) { - return mAppOpsItem; - } - - @Override public MenuItem getFeedbackMenuItem(Context context) { return mFeedbackItem; } @@ -264,7 +258,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl // Only show snooze for non-foreground notifications, and if the setting is on mSnoozeItem = createSnoozeItem(mContext); } - mAppOpsItem = createAppOpsItem(mContext); mFeedbackItem = createFeedbackItem(mContext); NotificationEntry entry = mParent.getEntry(); int personNotifType = mPeopleNotificationIdentifier @@ -281,7 +274,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl mRightMenuItems.add(mSnoozeItem); } mRightMenuItems.add(mInfoItem); - mRightMenuItems.add(mAppOpsItem); mRightMenuItems.add(mFeedbackItem); mLeftMenuItems.addAll(mRightMenuItems); @@ -690,14 +682,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl R.drawable.ic_settings); } - static MenuItem createAppOpsItem(Context context) { - AppOpsInfo appOpsContent = (AppOpsInfo) LayoutInflater.from(context).inflate( - R.layout.app_ops_info, null, false); - MenuItem info = new NotificationMenuItem(context, null, appOpsContent, - -1 /*don't show in slow swipe menu */); - return info; - } - static MenuItem createFeedbackItem(Context context) { FeedbackInfo feedbackContent = (FeedbackInfo) LayoutInflater.from(context).inflate( R.layout.feedback_info, null, false); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java index df8653cf2406..111b5756b6e8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java @@ -16,7 +16,7 @@ package com.android.systemui.statusbar.notification.row; -import javax.inject.Singleton; +import com.android.systemui.dagger.SysUISingleton; import dagger.Binds; import dagger.Module; @@ -30,7 +30,7 @@ public abstract class NotificationRowModule { * Provides notification row content binder instance. */ @Binds - @Singleton + @SysUISingleton public abstract NotificationRowContentBinder provideNotificationRowContentBinder( NotificationContentInflater contentBinderImpl); @@ -38,7 +38,7 @@ public abstract class NotificationRowModule { * Provides notification remote view cache instance. */ @Binds - @Singleton + @SysUISingleton public abstract NotifRemoteViewCache provideNotifRemoteViewCache( NotifRemoteViewCacheImpl cacheImpl); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/OnDismissCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallback.java index f1aed899e060..0c3f553825d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/OnDismissCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallback.java @@ -21,15 +21,21 @@ import android.service.notification.NotificationListenerService; import com.android.systemui.statusbar.notification.collection.NotificationEntry; /** - * Callback when a user clicks on an auto-cancelled notification or manually swipes to dismiss the - * notification. + * Callbacks for when a user interacts with an {@link ExpandableNotificationRow}. */ -public interface OnDismissCallback { +public interface OnUserInteractionCallback { /** - * Handle a user interaction that triggers a notification dismissal. + * Handle a user interaction that triggers a notification dismissal. Called when a user clicks + * on an auto-cancelled notification or manually swipes to dismiss the notification. */ void onDismiss( NotificationEntry entry, @NotificationListenerService.NotificationCancelReason int cancellationReason); + + /** + * Triggered after a user has changed the importance of the notification via its + * {@link NotificationGuts}. + */ + void onImportanceChanged(NotificationEntry entry); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java index c6f0a135cd34..3616f8faee1e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java @@ -20,13 +20,13 @@ import static com.android.systemui.statusbar.notification.row.NotificationRowCon import androidx.annotation.NonNull; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; import javax.inject.Inject; -import javax.inject.Singleton; /** * A stage that binds all content views for an already inflated {@link ExpandableNotificationRow}. @@ -34,7 +34,7 @@ import javax.inject.Singleton; * In the farther future, the binder logic and consequently this stage should be broken into * smaller stages. */ -@Singleton +@SysUISingleton public class RowContentBindStage extends BindStage<RowContentBindParams> { private final NotificationRowContentBinder mBinder; private final NotifInflationErrorManager mNotifInflationErrorManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java index 28ddf5971bcb..becc9a772b28 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java @@ -25,6 +25,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController; +import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.StatusBar; import dagger.Binds; @@ -55,6 +56,8 @@ public interface ExpandableNotificationRowComponent { Builder notificationEntry(NotificationEntry entry); @BindsInstance Builder onExpandClickListener(ExpandableNotificationRow.OnExpandClickListener presenter); + @BindsInstance + Builder listContainer(NotificationListContainer listContainer); ExpandableNotificationRowComponent build(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index 46517711a0dc..3f5867477f16 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.row.wrapper; import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y; -import android.app.AppOpsManager; import android.app.Notification; import android.content.Context; import android.util.ArraySet; @@ -64,10 +63,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { private TextView mHeaderText; private TextView mAppNameText; private ImageView mWorkProfileImage; - private View mCameraIcon; - private View mMicIcon; - private View mOverlayIcon; - private View mAppOps; private View mAudiblyAlertedIcon; private FrameLayout mIconContainer; private View mFeedbackIcon; @@ -109,7 +104,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { } }, TRANSFORMING_VIEW_TITLE); resolveHeaderViews(); - addAppOpsOnClickListener(row); addFeedbackOnClickListener(row); } @@ -121,10 +115,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button); mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge); mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header); - mCameraIcon = mView.findViewById(com.android.internal.R.id.camera); - mMicIcon = mView.findViewById(com.android.internal.R.id.mic); - mOverlayIcon = mView.findViewById(com.android.internal.R.id.overlay); - mAppOps = mView.findViewById(com.android.internal.R.id.app_ops); mAudiblyAlertedIcon = mView.findViewById(com.android.internal.R.id.alerted_icon); mFeedbackIcon = mView.findViewById(com.android.internal.R.id.feedback); if (mNotificationHeader != null) { @@ -133,38 +123,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { } } - private void addAppOpsOnClickListener(ExpandableNotificationRow row) { - View.OnClickListener listener = row.getAppOpsOnClickListener(); - if (mNotificationHeader != null) { - mNotificationHeader.setAppOpsOnClickListener(listener); - } - if (mAppOps != null) { - mAppOps.setOnClickListener(listener); - } - } - - /** - * Shows or hides 'app op in use' icons based on app usage. - */ - @Override - public void showAppOpsIcons(ArraySet<Integer> appOps) { - if (appOps == null) { - return; - } - if (mOverlayIcon != null) { - mOverlayIcon.setVisibility(appOps.contains(AppOpsManager.OP_SYSTEM_ALERT_WINDOW) - ? View.VISIBLE : View.GONE); - } - if (mCameraIcon != null) { - mCameraIcon.setVisibility(appOps.contains(AppOpsManager.OP_CAMERA) - ? View.VISIBLE : View.GONE); - } - if (mMicIcon != null) { - mMicIcon.setVisibility(appOps.contains(AppOpsManager.OP_RECORD_AUDIO) - ? View.VISIBLE : View.GONE); - } - } - private void addFeedbackOnClickListener(ExpandableNotificationRow row) { View.OnClickListener listener = row.getFeedbackOnClickListener(); if (mNotificationHeader != null) { @@ -304,15 +262,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, mHeaderText); } - if (mCameraIcon != null) { - mTransformationHelper.addViewTransformingToSimilar(mCameraIcon); - } - if (mMicIcon != null) { - mTransformationHelper.addViewTransformingToSimilar(mMicIcon); - } - if (mOverlayIcon != null) { - mTransformationHelper.addViewTransformingToSimilar(mOverlayIcon); - } if (mAudiblyAlertedIcon != null) { mTransformationHelper.addViewTransformingToSimilar(mAudiblyAlertedIcon); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java index 93d3f3bdbe96..33c939054be5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java @@ -22,14 +22,12 @@ import android.annotation.Nullable; import android.app.Notification; import android.content.Context; import android.content.res.ColorStateList; -import android.graphics.drawable.Drawable; import android.media.MediaMetadata; import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.metrics.LogMaker; import android.os.Handler; -import android.service.notification.StatusBarNotification; import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; @@ -43,13 +41,9 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.widget.MediaNotificationView; import com.android.systemui.Dependency; -import com.android.systemui.qs.QSPanel; -import com.android.systemui.qs.QuickQSPanel; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; -import com.android.systemui.util.Utils; import java.util.Timer; import java.util.TimerTask; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index 605fbc0f6125..42f5e389d5a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -97,14 +97,6 @@ public abstract class NotificationViewWrapper implements TransformableView { } /** - * Show a set of app opp icons in the layout. - * - * @param appOps which app ops to show - */ - public void showAppOpsIcons(ArraySet<Integer> appOps) { - } - - /** * Shows or hides feedback icon. */ public void showFeedbackIcon(boolean show) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt index 5757fe8040f0..32d41a8bfab7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt @@ -26,23 +26,21 @@ import android.service.notification.NotificationListenerService.REASON_GROUP_SUM import android.view.LayoutInflater import android.view.View import android.widget.LinearLayout - -import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.R +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController import com.android.systemui.statusbar.notification.NotificationEntryListener import com.android.systemui.statusbar.notification.NotificationEntryManager +import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.DungeonRow import com.android.systemui.util.Assert - import javax.inject.Inject -import javax.inject.Singleton /** * Controller for the bottom area of NotificationStackScrollLayout. It owns swiped-away foreground * service notifications and can reinstantiate them when requested. */ -@Singleton +@SysUISingleton class ForegroundServiceSectionController @Inject constructor( val entryManager: NotificationEntryManager, val featureController: ForegroundServiceDismissalFeatureController diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index 93c2377ccfae..a396305a49b6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -22,7 +22,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.drawable.ColorDrawable; import android.service.notification.StatusBarNotification; -import android.util.ArraySet; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.NotificationHeaderView; @@ -36,7 +35,7 @@ import com.android.systemui.R; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.NotificationHeaderUtil; import com.android.systemui.statusbar.notification.NotificationUtils; -import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.HybridGroupManager; import com.android.systemui.statusbar.notification.row.HybridNotificationView; @@ -1303,20 +1302,6 @@ public class NotificationChildrenContainer extends ViewGroup { } /** - * Show a set of app opp icons in the layout. - * - * @param appOps which app ops to show - */ - public void showAppOpsIcons(ArraySet<Integer> appOps) { - if (mNotificationHeaderWrapper != null) { - mNotificationHeaderWrapper.showAppOpsIcons(appOps); - } - if (mNotificationHeaderWrapperLowPriority != null) { - mNotificationHeaderWrapperLowPriority.showAppOpsIcons(appOps); - } - } - - /** * Shows or hides feedback icon. */ public void showFeedbackIcon(boolean show) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java index 2c3239a45012..fe6666943e5c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.stack; import android.util.MathUtils; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -28,12 +29,11 @@ import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import java.util.HashSet; import javax.inject.Inject; -import javax.inject.Singleton; /** * A class that manages the roundness for notification views */ -@Singleton +@SysUISingleton public class NotificationRoundnessManager implements OnHeadsUpChangedListener { private final ExpandableView[] mFirstInSectionViews; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt index 17b414379f8d..cb7dfe87f7fb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt @@ -16,15 +16,15 @@ package com.android.systemui.statusbar.notification.stack +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogLevel import com.android.systemui.log.dagger.NotificationSectionLog import javax.inject.Inject -import javax.inject.Singleton private const val TAG = "NotifSections" -@Singleton +@SysUISingleton class NotificationSectionsLogger @Inject constructor( @NotificationSectionLog private val logBuffer: LogBuffer ) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 57c1a16a259d..d4c270f45ceb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.stack; import static android.service.notification.NotificationStats.DISMISSAL_SHADE; import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; -import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT; import static com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.ANCHOR_SCROLLING; @@ -126,10 +125,10 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.ShadeViewRefactor; import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.logging.NotificationLogger; @@ -151,7 +150,6 @@ import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener; -import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; @@ -180,9 +178,7 @@ import javax.inject.Named; /** * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack. */ -public class NotificationStackScrollLayout extends ViewGroup implements ScrollAdapter, - NotificationListContainer, ConfigurationListener, Dumpable, - DynamicPrivacyController.Listener { +public class NotificationStackScrollLayout extends ViewGroup implements ScrollAdapter, Dumpable { public static final float BACKGROUND_ALPHA_DIMMED = 0.7f; private static final String TAG = "StackScroller"; @@ -210,11 +206,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private final Paint mBackgroundPaint = new Paint(); private final boolean mShouldDrawNotificationBackground; private boolean mHighPriorityBeforeSpeedBump; - private final boolean mAllowLongPress; private boolean mDismissRtl; private float mExpandedHeight; private int mOwnScrollY; + private View mScrollAnchorView; private int mScrollAnchorViewY; private int mMaxLayoutHeight; @@ -502,7 +498,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>(); private int mHeadsUpInset; private HeadsUpAppearanceController mHeadsUpAppearanceController; - private NotificationIconAreaController mIconAreaController; private final NotificationLockscreenUserManager mLockscreenUserManager; private final Rect mTmpRect = new Rect(); private final FeatureFlags mFeatureFlags; @@ -539,6 +534,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private int mGapHeight; private int mWaterfallTopInset; + private NotificationStackScrollLayoutController mController; private SysuiColorExtractor.OnColorsChangedListener mOnColorsChangedListener = (colorExtractor, which) -> { @@ -546,11 +542,23 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd updateDecorViews(useDarkText); }; + private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener = + new ExpandableView.OnHeightChangedListener() { + @Override + public void onHeightChanged(ExpandableView view, boolean needsAnimation) { + onChildHeightChanged(view, needsAnimation); + } + + @Override + public void onReset(ExpandableView view) { + onChildHeightReset(view); + } + }; + @Inject public NotificationStackScrollLayout( @Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, NotificationRoundnessManager notificationRoundnessManager, DynamicPrivacyController dynamicPrivacyController, SysuiStatusBarStateController statusBarStateController, @@ -573,14 +581,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd super(context, attrs, 0, 0); Resources res = getResources(); - mAllowLongPress = allowLongPress; - mRoundnessManager = notificationRoundnessManager; mLockscreenUserManager = notificationLockscreenUserManager; mNotificationGutsManager = notificationGutsManager; mHeadsUpManager = headsUpManager; - mHeadsUpManager.addListener(mRoundnessManager); mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed); mKeyguardBypassController = keyguardBypassController; mFalsingManager = falsingManager; @@ -612,9 +617,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd res.getBoolean(R.bool.config_drawNotificationBackground); mFadeNotificationsOnDismiss = res.getBoolean(R.bool.config_fadeNotificationsOnDismiss); - mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated); - mRoundnessManager.setOnRoundingChangedCallback(this::invalidate); - addOnExpandedHeightChangedListener(mRoundnessManager::setExpanded); mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener); setOutlineProvider(mOutlineProvider); @@ -641,13 +643,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd tunerService.addTunable((key, newValue) -> { if (key.equals(HIGH_PRIORITY)) { mHighPriorityBeforeSpeedBump = "1".equals(newValue); - } else if (key.equals(Settings.Secure.NOTIFICATION_DISMISS_RTL)) { - updateDismissRtlSetting("1".equals(newValue)); - } else if (key.equals(Settings.Secure.NOTIFICATION_HISTORY_ENABLED)) { - updateFooter(); } - }, HIGH_PRIORITY, Settings.Secure.NOTIFICATION_DISMISS_RTL, - Settings.Secure.NOTIFICATION_HISTORY_ENABLED); + }, HIGH_PRIORITY); mFeatureFlags = featureFlags; mNotifPipeline = notifPipeline; @@ -669,7 +666,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd }); } - dynamicPrivacyController.addListener(this); mDynamicPrivacyController = dynamicPrivacyController; mStatusbarStateController = statusBarStateController; initializeForegroundServiceSection(fgsFeatureController); @@ -697,7 +693,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } - private void updateDismissRtlSetting(boolean dismissRtl) { + void updateDismissRtlSetting(boolean dismissRtl) { mDismissRtl = dismissRtl; for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); @@ -715,9 +711,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd inflateEmptyShadeView(); inflateFooterView(); mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation); - if (mAllowLongPress) { - setLongPressListener(mNotificationGutsManager::openGuts); - } } /** @@ -735,36 +728,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return 0f; } - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void onDensityOrFontScaleChanged() { - reinflateViews(); - } - - private void reinflateViews() { + void reinflateViews() { inflateFooterView(); inflateEmptyShadeView(); updateFooter(); mSectionsManager.reinflateViews(LayoutInflater.from(mContext)); } - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void onThemeChanged() { - updateFooter(); - } - - @Override - public void onOverlayChanged() { - int newRadius = mContext.getResources().getDimensionPixelSize( - Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius)); - if (mCornerRadius != newRadius) { - mCornerRadius = newRadius; - invalidate(); - } - reinflateViews(); - } - @VisibleForTesting @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void updateFooter() { @@ -830,7 +800,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd super.onAttachedToWindow(); ((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class)) .addCallback(mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER); - Dependency.get(ConfigurationController.class).addCallback(this); } @Override @@ -838,18 +807,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd protected void onDetachedFromWindow() { super.onDetachedFromWindow(); Dependency.get(StatusBarStateController.class).removeCallback(mStateListener); - Dependency.get(ConfigurationController.class).removeCallback(this); } - @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public NotificationSwipeActionHelper getSwipeActionHelper() { return mSwipeHelper; } - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void onUiModeChanged() { + void updateBgColor() { mBgColor = mContext.getColor(R.color.notification_shade_background_color); updateBackgroundDimming(); mShelf.onUiModeChanged(); @@ -1082,6 +1047,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd R.dimen.heads_up_status_bar_padding); } + void updateCornerRadius() { + int newRadius = getResources().getDimensionPixelSize( + Utils.getThemeAttr(getContext(), android.R.attr.dialogCornerRadius)); + if (mCornerRadius != newRadius) { + mCornerRadius = newRadius; + invalidate(); + } + } + @ShadeViewRefactor(RefactorComponent.COORDINATOR) private void notifyHeightChangeListener(ExpandableView view) { notifyHeightChangeListener(view, false /* needsAnimation */); @@ -1157,14 +1131,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mNoAmbient = noAmbient; } - @Override @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) public void setChildLocationsChangedListener( NotificationLogger.OnChildLocationsChangedListener listener) { mListener = listener; } - @Override + public void setScrollAnchorView(View scrollAnchorView) { + mScrollAnchorView = scrollAnchorView; + } + @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM) public boolean isInVisibleLocation(NotificationEntry entry) { ExpandableNotificationRow row = entry.getRow(); @@ -1863,7 +1839,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } - @Override @ShadeViewRefactor(RefactorComponent.ADAPTER) public ViewGroup getViewParentForNotification(NotificationEntry entry) { return this; @@ -2553,7 +2528,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd previous); } - @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public boolean hasPulsingNotifications() { return mPulsing; @@ -3057,7 +3031,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - @Override public void cleanUpViewStateForEntry(NotificationEntry entry) { View child = entry.getRow(); if (child == mSwipeHelper.getTranslatingParentView()) { @@ -3324,7 +3297,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void onViewAdded(View child) { super.onViewAdded(child); - onViewAddedInternal((ExpandableView) child); + if (child instanceof ExpandableView) { + onViewAddedInternal((ExpandableView) child); + } } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -3357,9 +3332,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.COORDINATOR) - private void onViewAddedInternal(ExpandableView child) { + void onViewAddedInternal(ExpandableView child) { updateHideSensitiveForChild(child); - child.setOnHeightChangedListener(this); + child.setOnHeightChangedListener(mOnChildHeightChangedListener); generateAddAnimation(child, false /* fromMoreCard */); updateAnimationState(child); updateChronometerForChild(child); @@ -3381,18 +3356,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd child.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive()); } - @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) { onViewRemovedInternal(row, childrenContainer); } - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void notifyGroupChildAdded(ExpandableView row) { - onViewAddedInternal(row); - } - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) public void setAnimationsEnabled(boolean animationsEnabled) { mAnimationsEnabled = animationsEnabled; @@ -3417,33 +3385,29 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private void updateAnimationState(View child) { + void updateAnimationState(View child) { updateAnimationState((mAnimationsEnabled || hasPulsingNotifications()) && (mIsExpanded || isPinnedHeadsUp(child)), child); } - @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setExpandingNotification(ExpandableNotificationRow row) { + void setExpandingNotification(ExpandableNotificationRow row) { mAmbientState.setExpandingNotification(row); requestChildrenUpdate(); } - @Override @ShadeViewRefactor(RefactorComponent.ADAPTER) - public void bindRow(ExpandableNotificationRow row) { + void bindRow(ExpandableNotificationRow row) { row.setHeadsUpAnimatingAwayListener(animatingAway -> { mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway); mHeadsUpAppearanceController.updateHeader(row.getEntry()); }); } - @Override public boolean containsView(View v) { return v.getParent() == this; } - @Override @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) public void applyExpandAnimationParams(ExpandAnimationParameters params) { mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange()); @@ -3459,12 +3423,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - public boolean isAddOrRemoveAnimationPending() { + boolean isAddOrRemoveAnimationPending() { return mNeedsAnimation && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty()); } - @Override @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) { if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress && !isFullyHidden()) { @@ -3482,7 +3445,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } - @Override @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) public void changeViewPosition(ExpandableView child, int newIndex) { Assert.isMainThread(); @@ -4478,12 +4440,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.COORDINATOR) - public int getEmptyBottomMargin() { + int getEmptyBottomMargin() { return Math.max(mMaxLayoutHeight - mContentHeight, 0); } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void checkSnoozeLeavebehind() { + void checkSnoozeLeavebehind() { if (mCheckForLeavebehind) { mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, @@ -4493,19 +4455,19 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void resetCheckSnoozeLeavebehind() { + void resetCheckSnoozeLeavebehind() { mCheckForLeavebehind = true; } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - public void onExpansionStarted() { + void onExpansionStarted() { mIsExpansionChanging = true; mAmbientState.setExpansionChanging(true); checkSnoozeLeavebehind(); } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - public void onExpansionStopped() { + void onExpansionStopped() { mIsExpansionChanging = false; resetCheckSnoozeLeavebehind(); mAmbientState.setExpansionChanging(false); @@ -4554,20 +4516,20 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - public void onPanelTrackingStarted() { + void onPanelTrackingStarted() { mPanelTracking = true; mAmbientState.setPanelTracking(true); resetExposedMenuView(true /* animate */, true /* force */); } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - public void onPanelTrackingStopped() { + void onPanelTrackingStopped() { mPanelTracking = false; mAmbientState.setPanelTracking(false); } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - public void resetScrollPosition() { + void resetScrollPosition() { mScroller.abortAnimation(); if (ANCHOR_SCROLLING) { // TODO: once we're recycling this will need to modify the adapter position instead @@ -4608,15 +4570,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.COORDINATOR) - private void updateChronometerForChild(View child) { + void updateChronometerForChild(View child) { if (child instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) child; row.setChronometerRunning(mIsExpanded); } } - @Override - public void onHeightChanged(ExpandableView view, boolean needsAnimation) { + void onChildHeightChanged(ExpandableView view, boolean needsAnimation) { updateContentHeight(); updateScrollPositionOnExpandInBottom(view); clampScrollPosition(); @@ -4639,8 +4600,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd requestChildrenUpdate(); } - @Override - public void onReset(ExpandableView view) { + void onChildHeightReset(ExpandableView view) { updateAnimationState(view); updateChronometerForChild(view); } @@ -4681,13 +4641,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setOnHeightChangedListener( + void setOnHeightChangedListener( ExpandableView.OnHeightChangedListener onHeightChangedListener) { this.mOnHeightChangedListener = onHeightChangedListener; } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - public void onChildAnimationFinished() { + void onChildAnimationFinished() { setAnimationRunning(false); requestChildrenUpdate(); runAnimationFinishedRunnables(); @@ -4731,7 +4691,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * See {@link AmbientState#setDimmed}. */ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setDimmed(boolean dimmed, boolean animate) { + void setDimmed(boolean dimmed, boolean animate) { dimmed &= onKeyguard(); mAmbientState.setDimmed(dimmed); if (animate && mAnimationsEnabled) { @@ -4796,7 +4756,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * See {@link AmbientState#setActivatedChild}. */ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setActivatedChild(ActivatableNotificationView activatedChild) { + void setActivatedChild(ActivatableNotificationView activatedChild) { mAmbientState.setActivatedChild(activatedChild); if (mAnimationsEnabled) { mActivateNeedsAnimation = true; @@ -4872,7 +4832,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * @param lightTheme True if light theme should be used. */ @ShadeViewRefactor(RefactorComponent.DECORATOR) - public void updateDecorViews(boolean lightTheme) { + void updateDecorViews(boolean lightTheme) { if (lightTheme == mUsingLightTheme) { return; } @@ -4887,7 +4847,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void goToFullShade(long delay) { + void goToFullShade(long delay) { mGoToFullShadeNeedsAnimation = true; mGoToFullShadeDelay = delay; mNeedsAnimation = true; @@ -4900,13 +4860,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.COORDINATOR) - public void setIntrinsicPadding(int intrinsicPadding) { + void setIntrinsicPadding(int intrinsicPadding) { mIntrinsicPadding = intrinsicPadding; mAmbientState.setIntrinsicPadding(intrinsicPadding); } @ShadeViewRefactor(RefactorComponent.COORDINATOR) - public int getIntrinsicPadding() { + int getIntrinsicPadding() { return mIntrinsicPadding; } @@ -4940,7 +4900,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * animation curve. */ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setHideAmount(float linearHideAmount, float interpolatedHideAmount) { + void setHideAmount(float linearHideAmount, float interpolatedHideAmount) { mLinearHideAmount = linearHideAmount; mInterpolatedHideAmount = interpolatedHideAmount; boolean wasFullyHidden = mAmbientState.isFullyHidden(); @@ -4982,7 +4942,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - public void notifyHideAnimationStart(boolean hide) { + void notifyHideAnimationStart(boolean hide) { // We only swap the scaling factor if we're fully hidden or fully awake to avoid // interpolation issues when playing with the power button. if (mInterpolatedHideAmount == 0 || mInterpolatedHideAmount == 1) { @@ -5010,7 +4970,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setFooterView(@NonNull FooterView footerView) { + void setFooterView(@NonNull FooterView footerView) { int index = -1; if (mFooterView != null) { index = indexOfChild(mFooterView); @@ -5021,7 +4981,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setEmptyShadeView(EmptyShadeView emptyShadeView) { + void setEmptyShadeView(EmptyShadeView emptyShadeView) { int index = -1; if (mEmptyShadeView != null) { index = indexOfChild(mEmptyShadeView); @@ -5032,7 +4992,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void updateEmptyShadeView(boolean visible) { + void updateEmptyShadeView(boolean visible) { mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled); int oldTextRes = mEmptyShadeView.getTextResource(); @@ -5216,33 +5176,28 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } - @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public int getContainerChildCount() { return getChildCount(); } - @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public View getContainerChildAt(int i) { return getChildAt(i); } - @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void removeContainerView(View v) { Assert.isMainThread(); removeView(v); } - @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void addContainerView(View v) { Assert.isMainThread(); addView(v); } - @Override public void addContainerViewAt(View v, int index) { Assert.isMainThread(); addView(v, index); @@ -5284,7 +5239,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd requestChildrenUpdate(); } - @Override public void setWillExpand(boolean willExpand) { mWillExpand = willExpand; } @@ -5430,7 +5384,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd addView(mShelf, index); mAmbientState.setShelf(mShelf); mStateAnimator.setShelf(mShelf); - notificationShelfController.bind(mAmbientState, this); + notificationShelfController.bind(mAmbientState, mController); if (ANCHOR_SCROLLING) { mScrollAnchorView = mShelf; } @@ -5620,11 +5574,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setIconAreaController(NotificationIconAreaController controller) { - mIconAreaController = controller; - } - - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @VisibleForTesting void clearNotifications( @SelectedRows int selection, @@ -5749,7 +5698,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } - @Override public void setNotificationActivityStarter( NotificationActivityStarter notificationActivityStarter) { mNotificationActivityStarter = notificationActivityStarter; @@ -5815,10 +5763,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mNotificationPanelController = notificationPanelViewController; } - public void updateIconAreaViews() { - mIconAreaController.updateNotificationIcons(); - } - /** * Set how far the wake up is when waking up from pulsing. This is a height and will adjust the * notification positions accordingly. @@ -5877,17 +5821,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mDimmedNeedsAnimation = true; } - @Override - public void onDynamicPrivacyChanged() { - if (mIsExpanded) { - // The bottom might change because we're using the final actual height of the view - mAnimateBottomOnLayout = true; - } - // Let's update the footer once the notifications have been updated (in the next frame) - post(() -> { - updateFooter(); - updateSectionBoundaries("dynamic privacy changed"); - }); + void setAnimateBottomOnLayout(boolean animateBottomOnLayout) { + mAnimateBottomOnLayout = animateBottomOnLayout; } public void setOnPulseHeightChangedListener(Runnable listener) { @@ -5904,6 +5839,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return MathUtils.smoothStep(0, totalDistance, dragDownAmount); } + public void setController( + NotificationStackScrollLayoutController notificationStackScrollLayoutController) { + mController = notificationStackScrollLayoutController; + mController.getNoticationRoundessManager().setAnimatedChildren(mChildrenToAddAnimated); + } + + public NotificationStackScrollLayoutController getController() { + return mController; + } + /** * A listener that is notified when the empty space below the notifications is clicked on */ @@ -5998,7 +5943,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } - @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void resetExposedMenuView(boolean animate, boolean force) { mSwipeHelper.resetExposedMenuView(animate, force); @@ -6750,7 +6694,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } changedRow.setChildrenExpanded(expanded, animated); if (!mGroupExpandedForMeasure) { - onHeightChanged(changedRow, false /* needsAnimation */); + onChildHeightChanged(changedRow, false /* needsAnimation */); } runAfterAnimationFinished(new Runnable() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java new file mode 100644 index 000000000000..7c29ee2b5483 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -0,0 +1,774 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.statusbar.notification.stack; + +import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; + +import android.graphics.PointF; +import android.provider.Settings; +import android.view.Display; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsets; +import android.widget.FrameLayout; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; +import com.android.systemui.statusbar.NotificationShelfController; +import com.android.systemui.statusbar.RemoteInputController; +import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; +import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.NotificationActivityStarter; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.logging.NotificationLogger; +import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.ExpandableView; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; +import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; +import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; +import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.phone.NotificationPanelViewController; +import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import com.android.systemui.tuner.TunerService; + +import java.util.function.BiConsumer; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * Controller for {@link NotificationStackScrollLayout}. + */ +@StatusBarComponent.StatusBarScope +public class NotificationStackScrollLayoutController { + private final boolean mAllowLongPress; + private final NotificationGutsManager mNotificationGutsManager; + private final HeadsUpManagerPhone mHeadsUpManager; + private final NotificationRoundnessManager mNotificationRoundnessManager; + private final TunerService mTunerService; + private final DynamicPrivacyController mDynamicPrivacyController; + private final ConfigurationController mConfigurationController; + private final NotificationListContainerImpl mNotificationListContainer = + new NotificationListContainerImpl(); + private NotificationStackScrollLayout mView; + + @VisibleForTesting + final View.OnAttachStateChangeListener mOnAttachStateChangeListener = + new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + mConfigurationController.addCallback(mConfigurationListener); + } + + @Override + public void onViewDetachedFromWindow(View v) { + mConfigurationController.removeCallback(mConfigurationListener); + } + }; + + private final DynamicPrivacyController.Listener mDynamicPrivacyControllerListener = () -> { + if (mView.isExpanded()) { + // The bottom might change because we're using the final actual height of the view + mView.setAnimateBottomOnLayout(true); + } + // Let's update the footer once the notifications have been updated (in the next frame) + mView.post(() -> { + updateFooter(); + updateSectionBoundaries("dynamic privacy changed"); + }); + }; + + @VisibleForTesting + final ConfigurationListener mConfigurationListener = new ConfigurationListener() { + @Override + public void onDensityOrFontScaleChanged() { + mView.reinflateViews(); + } + + @Override + public void onOverlayChanged() { + mView.updateCornerRadius(); + mView.reinflateViews(); + } + + @Override + public void onUiModeChanged() { + mView.updateBgColor(); + } + + @Override + public void onThemeChanged() { + updateFooter(); + } + }; + + @Inject + public NotificationStackScrollLayoutController( + @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, + NotificationGutsManager notificationGutsManager, + HeadsUpManagerPhone headsUpManager, + NotificationRoundnessManager notificationRoundnessManager, + TunerService tunerService, + DynamicPrivacyController dynamicPrivacyController, + ConfigurationController configurationController) { + mAllowLongPress = allowLongPress; + mNotificationGutsManager = notificationGutsManager; + mHeadsUpManager = headsUpManager; + mNotificationRoundnessManager = notificationRoundnessManager; + mTunerService = tunerService; + mDynamicPrivacyController = dynamicPrivacyController; + mConfigurationController = configurationController; + } + + public void attach(NotificationStackScrollLayout view) { + mView = view; + mView.setController(this); + + if (mAllowLongPress) { + mView.setLongPressListener(mNotificationGutsManager::openGuts); + } + + mHeadsUpManager.addListener(mNotificationRoundnessManager); // TODO: why is this here? + mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener); + + mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate); + mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded); + + mTunerService.addTunable( + (key, newValue) -> { + if (key.equals(Settings.Secure.NOTIFICATION_DISMISS_RTL)) { + mView.updateDismissRtlSetting("1".equals(newValue)); + } else if (key.equals(Settings.Secure.NOTIFICATION_HISTORY_ENABLED)) { + updateFooter(); + } + }, + Settings.Secure.NOTIFICATION_DISMISS_RTL, + Settings.Secure.NOTIFICATION_HISTORY_ENABLED); + + if (mView.isAttachedToWindow()) { + mOnAttachStateChangeListener.onViewAttachedToWindow(mView); + } + mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener); + } + + public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) { + mView.addOnExpandedHeightChangedListener(listener); + } + + public void removeOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) { + mView.removeOnExpandedHeightChangedListener(listener); + } + + public void addOnLayoutChangeListener(View.OnLayoutChangeListener listener) { + mView.addOnLayoutChangeListener(listener); + } + + public void removeOnLayoutChangeListener(View.OnLayoutChangeListener listener) { + mView.removeOnLayoutChangeListener(listener); + } + + public void setHeadsUpAppearanceController(HeadsUpAppearanceController controller) { + mView.setHeadsUpAppearanceController(controller); + } + + public void requestLayout() { + mView.requestLayout(); + } + + public Display getDisplay() { + return mView.getDisplay(); + } + + public WindowInsets getRootWindowInsets() { + return mView.getRootWindowInsets(); + } + + public int getRight() { + return mView.getRight(); + } + + public boolean isLayoutRtl() { + return mView.isLayoutRtl(); + } + + public float getLeft() { + return mView.getLeft(); + } + + public float getTranslationX() { + return mView.getTranslationX(); + } + + public void setOnHeightChangedListener( + ExpandableView.OnHeightChangedListener listener) { + mView.setOnHeightChangedListener(listener); + } + + public void setOverscrollTopChangedListener( + NotificationStackScrollLayout.OnOverscrollTopChangedListener listener) { + mView.setOverscrollTopChangedListener(listener); + } + + public void setOnEmptySpaceClickListener( + NotificationStackScrollLayout.OnEmptySpaceClickListener listener) { + mView.setOnEmptySpaceClickListener(listener); + } + + public void setTrackingHeadsUp(ExpandableNotificationRow expandableNotificationRow) { + mView.setTrackingHeadsUp(expandableNotificationRow); + } + + public void wakeUpFromPulse() { + mView.wakeUpFromPulse(); + } + + public boolean isPulseExpanding() { + return mView.isPulseExpanding(); + } + + public void setOnPulseHeightChangedListener(Runnable listener) { + mView.setOnPulseHeightChangedListener(listener); + } + + public void setDozeAmount(float amount) { + mView.setDozeAmount(amount); + } + + public float getWakeUpHeight() { + return mView.getWakeUpHeight(); + } + + public void setHideAmount(float linearAmount, float amount) { + mView.setHideAmount(linearAmount, amount); + } + + public void notifyHideAnimationStart(boolean hide) { + mView.notifyHideAnimationStart(hide); + } + + public float setPulseHeight(float height) { + return mView.setPulseHeight(height); + } + + public void getLocationOnScreen(int[] outLocation) { + mView.getLocationOnScreen(outLocation); + } + + public ExpandableView getChildAtRawPosition(float x, float y) { + return mView.getChildAtRawPosition(x, y); + } + + public FrameLayout.LayoutParams getLayoutParams() { + return (FrameLayout.LayoutParams) mView.getLayoutParams(); + } + + public void setLayoutParams(FrameLayout.LayoutParams lp) { + mView.setLayoutParams(lp); + } + + public void setIsFullWidth(boolean isFullWidth) { + mView.setIsFullWidth(isFullWidth); + } + + public boolean isAddOrRemoveAnimationPending() { + return mView.isAddOrRemoveAnimationPending(); + } + + public int getVisibleNotificationCount() { + return mView.getVisibleNotificationCount(); + } + + public int getIntrinsicContentHeight() { + return mView.getIntrinsicContentHeight(); + } + + public void setIntrinsicPadding(int intrinsicPadding) { + mView.setIntrinsicPadding(intrinsicPadding); + } + + public int getHeight() { + return mView.getHeight(); + } + + public int getChildCount() { + return mView.getChildCount(); + } + + public ExpandableView getChildAt(int i) { + return (ExpandableView) mView.getChildAt(i); + } + + public void goToFullShade(long delay) { + mView.goToFullShade(delay); + } + + public void setOverScrollAmount(float amount, boolean onTop, boolean animate, + boolean cancelAnimators) { + mView.setOverScrollAmount(amount, onTop, animate, cancelAnimators); + } + + public void setOverScrollAmount(float amount, boolean onTop, boolean animate) { + mView.setOverScrollAmount(amount, onTop, animate); + } + + public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) { + mView.setOverScrolledPixels(numPixels, onTop, animate); + } + + public void resetScrollPosition() { + mView.resetScrollPosition(); + } + + public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) { + mView.setShouldShowShelfOnly(shouldShowShelfOnly); + } + + public void cancelLongPress() { + mView.cancelLongPress(); + } + + public float getX() { + return mView.getX(); + } + + public boolean isBelowLastNotification(float x, float y) { + return mView.isBelowLastNotification(x, y); + } + + public float getWidth() { + return mView.getWidth(); + } + + public float getOpeningHeight() { + return mView.getOpeningHeight(); + } + + public float getBottomMostNotificationBottom() { + return mView.getBottomMostNotificationBottom(); + } + + public void checkSnoozeLeavebehind() { + mView.checkSnoozeLeavebehind(); + } + + public void setQsExpanded(boolean expanded) { + mView.setQsExpanded(expanded); + } + + public void setScrollingEnabled(boolean enabled) { + mView.setScrollingEnabled(enabled); + } + + public void setQsExpansionFraction(float expansionFraction) { + mView.setQsExpansionFraction(expansionFraction); + } + + public float calculateAppearFractionBypass() { + return mView.calculateAppearFractionBypass(); + } + + public void updateTopPadding(float qsHeight, boolean animate) { + mView.updateTopPadding(qsHeight, animate); + } + + public void resetCheckSnoozeLeavebehind() { + mView.resetCheckSnoozeLeavebehind(); + } + + public boolean isScrolledToBottom() { + return mView.isScrolledToBottom(); + } + + public int getNotGoneChildCount() { + return mView.getNotGoneChildCount(); + } + + public float getIntrinsicPadding() { + return mView.getIntrinsicPadding(); + } + + public float getLayoutMinHeight() { + return mView.getLayoutMinHeight(); + } + + public int getEmptyBottomMargin() { + return mView.getEmptyBottomMargin(); + } + + public float getTopPaddingOverflow() { + return mView.getTopPaddingOverflow(); + } + + public int getTopPadding() { + return mView.getTopPadding(); + } + + public float getEmptyShadeViewHeight() { + return mView.getEmptyShadeViewHeight(); + } + + public void setAlpha(float alpha) { + mView.setAlpha(alpha); + } + + public float getCurrentOverScrollAmount(boolean top) { + return mView.getCurrentOverScrollAmount(top); + } + + public float getCurrentOverScrolledPixels(boolean top) { + return mView.getCurrentOverScrolledPixels(top); + } + + public float calculateAppearFraction(float height) { + return mView.calculateAppearFraction(height); + } + + public void onExpansionStarted() { + mView.onExpansionStarted(); + } + + public void onExpansionStopped() { + mView.onExpansionStopped(); + } + + public void onPanelTrackingStarted() { + mView.onPanelTrackingStarted(); + } + + public void onPanelTrackingStopped() { + mView.onPanelTrackingStopped(); + } + + public void setHeadsUpBoundaries(int height, int bottomBarHeight) { + mView.setHeadsUpBoundaries(height, bottomBarHeight); + } + + public void setUnlockHintRunning(boolean running) { + mView.setUnlockHintRunning(running); + } + + public float getPeekHeight() { + return mView.getPeekHeight(); + } + + public boolean isFooterViewNotGone() { + return mView.isFooterViewNotGone(); + } + + public boolean isFooterViewContentVisible() { + return mView.isFooterViewContentVisible(); + } + + public int getFooterViewHeightWithPadding() { + return mView.getFooterViewHeightWithPadding(); + } + + public void updateEmptyShadeView(boolean visible) { + mView.updateEmptyShadeView(visible); + } + + public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { + mView.setHeadsUpAnimatingAway(headsUpAnimatingAway); + } + + public HeadsUpTouchHelper.Callback getHeadsUpCallback() { + return mView.getHeadsUpCallback(); + } + + public void forceNoOverlappingRendering(boolean force) { + mView.forceNoOverlappingRendering(force); + } + + public void setTranslationX(float translation) { + mView.setTranslationX(translation); + } + + public void setExpandingVelocity(float velocity) { + mView.setExpandingVelocity(velocity); + } + + public void setExpandedHeight(float expandedHeight) { + mView.setExpandedHeight(expandedHeight); + } + + public void setQsContainer(ViewGroup view) { + mView.setQsContainer(view); + } + + public void setAnimationsEnabled(boolean enabled) { + mView.setAnimationsEnabled(enabled); + } + + public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) { + mView.setDozing(dozing, animate, wakeUpTouchLocation); + } + + public void setPulsing(boolean pulsing, boolean animatePulse) { + mView.setPulsing(pulsing, animatePulse); + } + + public boolean hasActiveClearableNotifications( + @NotificationStackScrollLayout.SelectedRows int selection) { + return mView.hasActiveClearableNotifications(selection); + } + + public RemoteInputController.Delegate createDelegate() { + return mView.createDelegate(); + } + + public void updateSectionBoundaries(String reason) { + mView.updateSectionBoundaries(reason); + } + + public void updateSpeedBumpIndex() { + mView.updateSpeedBumpIndex(); + } + + public void updateFooter() { + mView.updateFooter(); + } + + public void onUpdateRowStates() { + mView.onUpdateRowStates(); + } + + public ActivatableNotificationView getActivatedChild() { + return mView.getActivatedChild(); + } + + public void setActivatedChild(ActivatableNotificationView view) { + mView.setActivatedChild(view); + } + + public void runAfterAnimationFinished(Runnable r) { + mView.runAfterAnimationFinished(r); + } + + public void setNotificationPanelController( + NotificationPanelViewController notificationPanelViewController) { + mView.setNotificationPanelController(notificationPanelViewController); + } + + public void setStatusBar(StatusBar statusBar) { + mView.setStatusBar(statusBar); + } + + public void setGroupManager(NotificationGroupManager groupManager) { + mView.setGroupManager(groupManager); + } + + public void setShelfController(NotificationShelfController notificationShelfController) { + mView.setShelfController(notificationShelfController); + } + + public void setScrimController(ScrimController scrimController) { + mView.setScrimController(scrimController); + } + + public ExpandableView getFirstChildNotGone() { + return mView.getFirstChildNotGone(); + } + + public void setInHeadsUpPinnedMode(boolean inPinnedMode) { + mView.setInHeadsUpPinnedMode(inPinnedMode); + } + + public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) { + mView.generateHeadsUpAnimation(entry, isHeadsUp); + } + + public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) { + mView.generateHeadsUpAnimation(row, isHeadsUp); + } + + public void setMaxTopPadding(int padding) { + mView.setMaxTopPadding(padding); + } + + public int getTransientViewCount() { + return mView.getTransientViewCount(); + } + + public View getTransientView(int i) { + return mView.getTransientView(i); + } + + public int getPositionInLinearLayout(ExpandableView row) { + return mView.getPositionInLinearLayout(row); + } + + public NotificationStackScrollLayout getView() { + return mView; + } + + public float calculateGapHeight(ExpandableView previousView, ExpandableView child, int count) { + return mView.calculateGapHeight(previousView, child, count); + } + + public NotificationRoundnessManager getNoticationRoundessManager() { + return mNotificationRoundnessManager; + } + + public NotificationListContainer getNotificationListContainer() { + return mNotificationListContainer; + } + + private class NotificationListContainerImpl implements NotificationListContainer { + @Override + public void setChildTransferInProgress(boolean childTransferInProgress) { + mView.setChildTransferInProgress(childTransferInProgress); + } + + @Override + public void changeViewPosition(ExpandableView child, int newIndex) { + mView.changeViewPosition(child, newIndex); + } + + @Override + public void notifyGroupChildAdded(ExpandableView row) { + mView.onViewAddedInternal(row); + } + + @Override + public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) { + mView.notifyGroupChildRemoved(row, childrenContainer); + } + + @Override + public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) { + mView.generateAddAnimation(child, fromMoreCard); + } + + @Override + public void generateChildOrderChangedEvent() { + mView.generateChildOrderChangedEvent(); + } + + @Override + public int getContainerChildCount() { + return mView.getContainerChildCount(); + } + + @Override + public void setNotificationActivityStarter( + NotificationActivityStarter notificationActivityStarter) { + mView.setNotificationActivityStarter(notificationActivityStarter); + } + + @Override + public View getContainerChildAt(int i) { + return mView.getContainerChildAt(i); + } + + @Override + public void removeContainerView(View v) { + mView.removeContainerView(v); + } + + @Override + public void addContainerView(View v) { + mView.addContainerView(v); + } + + @Override + public void addContainerViewAt(View v, int index) { + mView.addContainerViewAt(v, index); + } + + @Override + public void setMaxDisplayedNotifications(int maxNotifications) { + mView.setMaxDisplayedNotifications(maxNotifications); + } + + @Override + public ViewGroup getViewParentForNotification(NotificationEntry entry) { + return mView.getViewParentForNotification(entry); + } + + @Override + public void resetExposedMenuView(boolean animate, boolean force) { + mView.resetExposedMenuView(animate, force); + } + + @Override + public NotificationSwipeActionHelper getSwipeActionHelper() { + return mView.getSwipeActionHelper(); + } + + @Override + public void cleanUpViewStateForEntry(NotificationEntry entry) { + mView.cleanUpViewStateForEntry(entry); + } + + @Override + public void setChildLocationsChangedListener( + NotificationLogger.OnChildLocationsChangedListener listener) { + mView.setChildLocationsChangedListener(listener); + } + + public boolean hasPulsingNotifications() { + return mView.hasPulsingNotifications(); + } + + @Override + public boolean isInVisibleLocation(NotificationEntry entry) { + return mView.isInVisibleLocation(entry); + } + + @Override + public void onHeightChanged(ExpandableView view, boolean needsAnimation) { + mView.onChildHeightChanged(view, needsAnimation); + } + + @Override + public void onReset(ExpandableView view) { + mView.onChildHeightReset(view); + } + + @Override + public void bindRow(ExpandableNotificationRow row) { + mView.bindRow(row); + } + + @Override + public void applyExpandAnimationParams( + ActivityLaunchAnimator.ExpandAnimationParameters params) { + mView.applyExpandAnimationParams(params); + } + + @Override + public void setExpandingNotification(ExpandableNotificationRow row) { + mView.setExpandingNotification(row); + } + + @Override + public boolean containsView(View v) { + return mView.containsView(v); + } + + @Override + public void setWillExpand(boolean willExpand) { + mView.setWillExpand(willExpand); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppButtonData.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppButtonData.java deleted file mode 100644 index f6c1062f6749..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppButtonData.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2015 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 com.android.systemui.statusbar.phone; - -import android.app.ActivityManager.RecentTaskInfo; - -import java.util.ArrayList; - -/** - * Data associated with an app button. - */ -class AppButtonData { - public final AppInfo appInfo; - public boolean pinned; - // Recent tasks for this app, sorted by lastActiveTime, descending. - public ArrayList<RecentTaskInfo> tasks; - - public AppButtonData(AppInfo appInfo, boolean pinned) { - this.appInfo = appInfo; - this.pinned = pinned; - } - - public int getTaskCount() { - return tasks == null ? 0 : tasks.size(); - } - - /** - * Returns true if the button contains no useful information and should be removed. - */ - public boolean isEmpty() { - return !pinned && getTaskCount() == 0; - } - - public void addTask(RecentTaskInfo task) { - if (tasks == null) { - tasks = new ArrayList<RecentTaskInfo>(); - } - tasks.add(task); - } - - public void clearTasks() { - if (tasks != null) { - tasks.clear(); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppInfo.java deleted file mode 100644 index 8f0b532eabee..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppInfo.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2015 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 com.android.systemui.statusbar.phone; - -import android.content.ComponentName; -import android.os.UserHandle; - -/** - * Navigation bar app information. - */ -class AppInfo { - private final ComponentName mComponentName; - private final UserHandle mUser; - - public AppInfo(ComponentName componentName, UserHandle user) { - if (componentName == null || user == null) throw new IllegalArgumentException(); - mComponentName = componentName; - mUser = user; - } - - public ComponentName getComponentName() { - return mComponentName; - } - - public UserHandle getUser() { - return mUser; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final AppInfo other = (AppInfo) obj; - return mComponentName.equals(other.mComponentName) && mUser.equals(other.mUser); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java index d6039af9232a..aeb2efd2026a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java @@ -138,7 +138,7 @@ public class AutoHideController { mHandler.postDelayed(mAutoHide, AUTO_HIDE_TIMEOUT_MS); } - void checkUserAutoHide(MotionEvent event) { + public void checkUserAutoHide(MotionEvent event) { boolean shouldHide = isAnyTransientBarShown() && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar. && event.getX() == 0 && event.getY() == 0; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 0e76c904f8cd..e99637867220 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -40,6 +40,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.KeyguardViewController; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; @@ -47,6 +48,7 @@ import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.KeyguardStateController; import java.io.FileDescriptor; @@ -57,12 +59,11 @@ import java.util.Map; import java.util.Optional; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controller which coordinates all the biometric unlocking actions with the UI. */ -@Singleton +@SysUISingleton public class BiometricUnlockController extends KeyguardUpdateMonitorCallback implements Dumpable { private static final String TAG = "BiometricUnlockCtrl"; @@ -157,11 +158,11 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp private DozeScrimController mDozeScrimController; private KeyguardViewMediator mKeyguardViewMediator; private ScrimController mScrimController; - private StatusBar mStatusBar; private PendingAuthenticated mPendingAuthenticated = null; private boolean mPendingShowBouncer; private boolean mHasScreenTurnedOnSinceAuthenticating; private boolean mFadedAwayAfterWakeAndUnlock; + private BiometricModeListener mBiometricModeListener; private final MetricsLogger mMetricsLogger; @@ -243,7 +244,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp @Inject public BiometricUnlockController(Context context, DozeScrimController dozeScrimController, KeyguardViewMediator keyguardViewMediator, ScrimController scrimController, - StatusBar statusBar, ShadeController shadeController, + ShadeController shadeController, NotificationShadeWindowController notificationShadeWindowController, KeyguardStateController keyguardStateController, Handler handler, KeyguardUpdateMonitor keyguardUpdateMonitor, @@ -264,7 +265,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mDozeScrimController = dozeScrimController; mKeyguardViewMediator = keyguardViewMediator; mScrimController = scrimController; - mStatusBar = statusBar; mKeyguardStateController = keyguardStateController; mHandler = handler; mWakeUpDelay = resources.getInteger(com.android.internal.R.integer.config_wakeUpDelayDoze); @@ -278,6 +278,11 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mKeyguardViewController = keyguardViewController; } + /** Sets a {@link BiometricModeListener}. */ + public void setBiometricModeListener(BiometricModeListener biometricModeListener) { + mBiometricModeListener = biometricModeListener; + } + private final Runnable mReleaseBiometricWakeLockRunnable = new Runnable() { @Override public void run() { @@ -434,19 +439,25 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp } else { mKeyguardViewMediator.onWakeAndUnlocking(); } - if (mStatusBar.getNavigationBarView() != null) { - mStatusBar.getNavigationBarView().setWakeAndUnlocking(true); - } Trace.endSection(); break; case MODE_ONLY_WAKE: case MODE_NONE: break; } - mStatusBar.notifyBiometricAuthModeChanged(); + onModeChanged(mMode); + if (mBiometricModeListener != null) { + mBiometricModeListener.notifyBiometricAuthModeChanged(); + } Trace.endSection(); } + private void onModeChanged(@WakeAndUnlockMode int mode) { + if (mBiometricModeListener != null) { + mBiometricModeListener.onModeChanged(mode); + } + } + private void showBouncer() { if (mMode == MODE_SHOW_BOUNCER) { mKeyguardViewController.showBouncer(false); @@ -619,10 +630,10 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mMode = MODE_NONE; mBiometricType = null; mNotificationShadeWindowController.setForceDozeBrightness(false); - if (mStatusBar.getNavigationBarView() != null) { - mStatusBar.getNavigationBarView().setWakeAndUnlocking(false); + if (mBiometricModeListener != null) { + mBiometricModeListener.onResetMode(); + mBiometricModeListener.notifyBiometricAuthModeChanged(); } - mStatusBar.notifyBiometricAuthModeChanged(); } @VisibleForTesting @@ -702,4 +713,14 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp return 3; } } + + /** An interface to interact with the {@link BiometricUnlockController}. */ + public interface BiometricModeListener { + /** Called when {@code mMode} is reset to {@link #MODE_NONE}. */ + void onResetMode(); + /** Called when {@code mMode} has changed in {@link #startWakeAndUnlock(int)}. */ + void onModeChanged(@WakeAndUnlockMode int mode); + /** Called after processing {@link #onModeChanged(int)}. */ + void notifyBiometricAuthModeChanged(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java index ef0f7cddba24..f25359e5f481 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java @@ -14,7 +14,6 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.plugins.DarkIconDispatcher.DEFAULT_ICON_TINT; import static com.android.systemui.plugins.DarkIconDispatcher.getTint; import android.animation.ArgbEvaluator; @@ -25,18 +24,17 @@ import android.util.ArrayMap; import android.widget.ImageView; import com.android.systemui.R; -import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.CommandQueue; import java.io.FileDescriptor; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Singleton; /** */ -@Singleton +@SysUISingleton public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher, LightBarTransitionsController.DarkIntensityApplier { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java index 0731a568ae7d..31965d4fc4cd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java @@ -27,8 +27,8 @@ import android.view.ViewGroup; import android.widget.LinearLayout; import com.android.internal.statusbar.StatusBarIcon; -import com.android.systemui.DemoMode; import com.android.systemui.R; +import com.android.systemui.demomode.DemoMode; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.StatusBarIconView; @@ -39,7 +39,9 @@ import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconStat import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import java.util.ArrayList; +import java.util.List; +//TODO: This should be a controller, not its own view public class DemoStatusIcons extends StatusIconContainer implements DemoMode, DarkReceiver { private static final String TAG = "DemoStatusIcons"; @@ -90,73 +92,84 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da } @Override + public List<String> demoCommands() { + List<String> commands = new ArrayList<>(); + commands.add(COMMAND_STATUS); + return commands; + } + + @Override + public void onDemoModeStarted() { + mDemoMode = true; + mStatusIcons.setVisibility(View.GONE); + setVisibility(View.VISIBLE); + } + + @Override + public void onDemoModeFinished() { + mDemoMode = false; + mStatusIcons.setVisibility(View.VISIBLE); + setVisibility(View.GONE); + } + + @Override public void dispatchDemoCommand(String command, Bundle args) { - if (!mDemoMode && command.equals(COMMAND_ENTER)) { - mDemoMode = true; - mStatusIcons.setVisibility(View.GONE); - setVisibility(View.VISIBLE); - } else if (mDemoMode && command.equals(COMMAND_EXIT)) { - mDemoMode = false; - mStatusIcons.setVisibility(View.VISIBLE); - setVisibility(View.GONE); - } else if (mDemoMode && command.equals(COMMAND_STATUS)) { - String volume = args.getString("volume"); - if (volume != null) { - int iconId = volume.equals("vibrate") ? R.drawable.stat_sys_ringer_vibrate - : 0; - updateSlot("volume", null, iconId); - } - String zen = args.getString("zen"); - if (zen != null) { - int iconId = zen.equals("dnd") ? R.drawable.stat_sys_dnd : 0; - updateSlot("zen", null, iconId); - } - String bt = args.getString("bluetooth"); - if (bt != null) { - int iconId = bt.equals("connected") - ? R.drawable.stat_sys_data_bluetooth_connected : 0; - updateSlot("bluetooth", null, iconId); - } - String location = args.getString("location"); - if (location != null) { - int iconId = location.equals("show") ? PhoneStatusBarPolicy.LOCATION_STATUS_ICON_ID - : 0; - updateSlot("location", null, iconId); - } - String alarm = args.getString("alarm"); - if (alarm != null) { - int iconId = alarm.equals("show") ? R.drawable.stat_sys_alarm - : 0; - updateSlot("alarm_clock", null, iconId); - } - String tty = args.getString("tty"); - if (tty != null) { - int iconId = tty.equals("show") ? R.drawable.stat_sys_tty_mode - : 0; - updateSlot("tty", null, iconId); - } - String mute = args.getString("mute"); - if (mute != null) { - int iconId = mute.equals("show") ? android.R.drawable.stat_notify_call_mute - : 0; - updateSlot("mute", null, iconId); - } - String speakerphone = args.getString("speakerphone"); - if (speakerphone != null) { - int iconId = speakerphone.equals("show") ? android.R.drawable.stat_sys_speakerphone - : 0; - updateSlot("speakerphone", null, iconId); - } - String cast = args.getString("cast"); - if (cast != null) { - int iconId = cast.equals("show") ? R.drawable.stat_sys_cast : 0; - updateSlot("cast", null, iconId); - } - String hotspot = args.getString("hotspot"); - if (hotspot != null) { - int iconId = hotspot.equals("show") ? R.drawable.stat_sys_hotspot : 0; - updateSlot("hotspot", null, iconId); - } + String volume = args.getString("volume"); + if (volume != null) { + int iconId = volume.equals("vibrate") ? R.drawable.stat_sys_ringer_vibrate + : 0; + updateSlot("volume", null, iconId); + } + String zen = args.getString("zen"); + if (zen != null) { + int iconId = zen.equals("dnd") ? R.drawable.stat_sys_dnd : 0; + updateSlot("zen", null, iconId); + } + String bt = args.getString("bluetooth"); + if (bt != null) { + int iconId = bt.equals("connected") + ? R.drawable.stat_sys_data_bluetooth_connected : 0; + updateSlot("bluetooth", null, iconId); + } + String location = args.getString("location"); + if (location != null) { + int iconId = location.equals("show") ? PhoneStatusBarPolicy.LOCATION_STATUS_ICON_ID + : 0; + updateSlot("location", null, iconId); + } + String alarm = args.getString("alarm"); + if (alarm != null) { + int iconId = alarm.equals("show") ? R.drawable.stat_sys_alarm + : 0; + updateSlot("alarm_clock", null, iconId); + } + String tty = args.getString("tty"); + if (tty != null) { + int iconId = tty.equals("show") ? R.drawable.stat_sys_tty_mode + : 0; + updateSlot("tty", null, iconId); + } + String mute = args.getString("mute"); + if (mute != null) { + int iconId = mute.equals("show") ? android.R.drawable.stat_notify_call_mute + : 0; + updateSlot("mute", null, iconId); + } + String speakerphone = args.getString("speakerphone"); + if (speakerphone != null) { + int iconId = speakerphone.equals("show") ? android.R.drawable.stat_sys_speakerphone + : 0; + updateSlot("speakerphone", null, iconId); + } + String cast = args.getString("cast"); + if (cast != null) { + int iconId = cast.equals("show") ? R.drawable.stat_sys_cast : 0; + updateSlot("cast", null, iconId); + } + String hotspot = args.getString("hotspot"); + if (hotspot != null) { + int iconId = hotspot.equals("show") ? R.drawable.stat_sys_hotspot : 0; + updateSlot("hotspot", null, iconId); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index 5fab4bea9a04..64951448a543 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -25,6 +25,7 @@ import android.provider.Settings; import android.util.MathUtils; import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.AlwaysOnDisplayPolicy; import com.android.systemui.doze.DozeScreenState; @@ -34,12 +35,11 @@ import com.android.systemui.tuner.TunerService; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Singleton; /** * Retrieve doze information */ -@Singleton +@SysUISingleton public class DozeParameters implements TunerService.Tunable, com.android.systemui.plugins.statusbar.DozeParameters { private static final int MAX_DURATION = 60 * 1000; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java index e7d6eba1dcb3..b2cf72aca864 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java @@ -22,18 +22,18 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controller which handles all the doze animations of the scrims. */ -@Singleton +@SysUISingleton public class DozeScrimController implements StateListener { private static final String TAG = "DozeScrimController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java index 4afeba8de211..efb24693beff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java @@ -31,16 +31,18 @@ import android.view.View; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.assist.AssistManager; +import com.android.systemui.biometrics.AuthController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -48,14 +50,13 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; /** * Implementation of DozeHost for SystemUI. */ -@Singleton +@SysUISingleton public final class DozeServiceHost implements DozeHost { private static final String TAG = "DozeServiceHost"; private final ArrayList<Callback> mCallbacks = new ArrayList<>(); @@ -81,12 +82,12 @@ public final class DozeServiceHost implements DozeHost { private final Lazy<AssistManager> mAssistManagerLazy; private final DozeScrimController mDozeScrimController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final VisualStabilityManager mVisualStabilityManager; private final PulseExpansionHandler mPulseExpansionHandler; private final NotificationShadeWindowController mNotificationShadeWindowController; private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator; private NotificationShadeWindowViewController mNotificationShadeWindowViewController; private final LockscreenLockIconController mLockscreenLockIconController; + private final AuthController mAuthController; private NotificationIconAreaController mNotificationIconAreaController; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private NotificationPanelViewController mNotificationPanel; @@ -105,11 +106,12 @@ public final class DozeServiceHost implements DozeHost { KeyguardViewMediator keyguardViewMediator, Lazy<AssistManager> assistManagerLazy, DozeScrimController dozeScrimController, KeyguardUpdateMonitor keyguardUpdateMonitor, - VisualStabilityManager visualStabilityManager, PulseExpansionHandler pulseExpansionHandler, NotificationShadeWindowController notificationShadeWindowController, NotificationWakeUpCoordinator notificationWakeUpCoordinator, - LockscreenLockIconController lockscreenLockIconController) { + LockscreenLockIconController lockscreenLockIconController, + AuthController authController, + NotificationIconAreaController notificationIconAreaController) { super(); mDozeLog = dozeLog; mPowerManager = powerManager; @@ -124,11 +126,12 @@ public final class DozeServiceHost implements DozeHost { mAssistManagerLazy = assistManagerLazy; mDozeScrimController = dozeScrimController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mVisualStabilityManager = visualStabilityManager; mPulseExpansionHandler = pulseExpansionHandler; mNotificationShadeWindowController = notificationShadeWindowController; mNotificationWakeUpCoordinator = notificationWakeUpCoordinator; mLockscreenLockIconController = lockscreenLockIconController; + mAuthController = authController; + mNotificationIconAreaController = notificationIconAreaController; } // TODO: we should try to not pass status bar in here if we can avoid it. @@ -136,13 +139,13 @@ public final class DozeServiceHost implements DozeHost { /** * Initialize instance with objects only available later during execution. */ - public void initialize(StatusBar statusBar, - NotificationIconAreaController notificationIconAreaController, + public void initialize( + StatusBar statusBar, StatusBarKeyguardViewManager statusBarKeyguardViewManager, NotificationShadeWindowViewController notificationShadeWindowViewController, - NotificationPanelViewController notificationPanel, View ambientIndicationContainer) { + NotificationPanelViewController notificationPanel, + View ambientIndicationContainer) { mStatusBar = statusBar; - mNotificationIconAreaController = notificationIconAreaController; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mNotificationPanel = notificationPanel; mNotificationShadeWindowViewController = notificationShadeWindowViewController; @@ -254,11 +257,10 @@ public final class DozeServiceHost implements DozeHost { } private void setPulsing(boolean pulsing) { - mStatusBarStateController.setPulsing(pulsing); mStatusBarKeyguardViewManager.setPulsing(pulsing); mKeyguardViewMediator.setPulsing(pulsing); mNotificationPanel.setPulsing(pulsing); - mVisualStabilityManager.setPulsing(pulsing); + mStatusBarStateController.setPulsing(pulsing); mIgnoreTouchWhilePulsing = false; if (mKeyguardUpdateMonitor != null && passiveAuthInterrupt) { mKeyguardUpdateMonitor.onAuthInterruptDetected(pulsing /* active */); @@ -296,6 +298,7 @@ public final class DozeServiceHost implements DozeHost { @Override public void dozeTimeTick() { mNotificationPanel.dozeTimeTick(); + mAuthController.dozeTimeTick(); if (mAmbientIndicationContainer instanceof DozeReceiver) { ((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 51c02c9f93ab..8cdaa63994e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; @@ -52,7 +52,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, public static final int CONTENT_FADE_DELAY = 100; private final NotificationIconAreaController mNotificationIconAreaController; private final HeadsUpManagerPhone mHeadsUpManager; - private final NotificationStackScrollLayout mStackScroller; + private final NotificationStackScrollLayoutController mStackScrollerController; private final HeadsUpStatusBarView mHeadsUpStatusBarView; private final View mCenteredIconView; private final View mClockView; @@ -93,7 +93,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, public HeadsUpAppearanceController( NotificationIconAreaController notificationIconAreaController, HeadsUpManagerPhone headsUpManager, - View notificationShadeView, + NotificationStackScrollLayoutController notificationStackScrollLayoutController, SysuiStatusBarStateController statusBarStateController, KeyguardBypassController keyguardBypassController, KeyguardStateController keyguardStateController, @@ -101,10 +101,9 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, NotificationPanelViewController notificationPanelViewController, View statusBarView) { this(notificationIconAreaController, headsUpManager, statusBarStateController, keyguardBypassController, wakeUpCoordinator, keyguardStateController, - commandQueue, - statusBarView.findViewById(R.id.heads_up_status_bar_view), - notificationShadeView.findViewById(R.id.notification_stack_scroller), + commandQueue, notificationStackScrollLayoutController, notificationPanelViewController, + statusBarView.findViewById(R.id.heads_up_status_bar_view), statusBarView.findViewById(R.id.clock), statusBarView.findViewById(R.id.operator_name_frame), statusBarView.findViewById(R.id.centered_icon_area)); @@ -119,9 +118,9 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, NotificationWakeUpCoordinator wakeUpCoordinator, KeyguardStateController keyguardStateController, CommandQueue commandQueue, - HeadsUpStatusBarView headsUpStatusBarView, - NotificationStackScrollLayout stackScroller, + NotificationStackScrollLayoutController stackScrollerController, NotificationPanelViewController notificationPanelViewController, + HeadsUpStatusBarView headsUpStatusBarView, View clockView, View operatorNameView, View centeredIconView) { @@ -132,14 +131,14 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, mCenteredIconView = centeredIconView; headsUpStatusBarView.setOnDrawingRectChangedListener( () -> updateIsolatedIconLocation(true /* requireUpdate */)); - mStackScroller = stackScroller; + mStackScrollerController = stackScrollerController; mNotificationPanelViewController = notificationPanelViewController; notificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp); notificationPanelViewController.addVerticalTranslationListener(mUpdatePanelTranslation); notificationPanelViewController.setHeadsUpAppearanceController(this); - mStackScroller.addOnExpandedHeightChangedListener(mSetExpandedHeight); - mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener); - mStackScroller.setHeadsUpAppearanceController(this); + mStackScrollerController.addOnExpandedHeightChangedListener(mSetExpandedHeight); + mStackScrollerController.addOnLayoutChangeListener(mStackScrollLayoutChangeListener); + mStackScrollerController.setHeadsUpAppearanceController(this); mClockView = clockView; mOperatorNameView = operatorNameView; mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class); @@ -153,7 +152,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, updateTopEntry(); // trigger scroller to notify the latest panel translation - mStackScroller.requestLayout(); + mStackScrollerController.requestLayout(); } mHeadsUpStatusBarView.removeOnLayoutChangeListener(this); } @@ -174,8 +173,8 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, mNotificationPanelViewController.removeTrackingHeadsUpListener(mSetTrackingHeadsUp); mNotificationPanelViewController.removeVerticalTranslationListener(mUpdatePanelTranslation); mNotificationPanelViewController.setHeadsUpAppearanceController(null); - mStackScroller.removeOnExpandedHeightChangedListener(mSetExpandedHeight); - mStackScroller.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener); + mStackScrollerController.removeOnExpandedHeightChangedListener(mSetExpandedHeight); + mStackScrollerController.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener); mDarkIconDispatcher.removeDarkReceiver(this); } @@ -219,12 +218,12 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, } int realDisplaySize = 0; - if (mStackScroller.getDisplay() != null) { - mStackScroller.getDisplay().getRealSize(mPoint); + if (mStackScrollerController.getDisplay() != null) { + mStackScrollerController.getDisplay().getRealSize(mPoint); realDisplaySize = mPoint.x; } - WindowInsets windowInset = mStackScroller.getRootWindowInsets(); + WindowInsets windowInset = mStackScrollerController.getRootWindowInsets(); DisplayCutout cutout = (windowInset != null) ? windowInset.getDisplayCutout() : null; int sysWinLeft = (windowInset != null) ? windowInset.getStableInsetLeft() : 0; int sysWinRight = (windowInset != null) ? windowInset.getStableInsetRight() : 0; @@ -233,17 +232,17 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, int leftInset = Math.max(sysWinLeft, cutoutLeft); int rightInset = Math.max(sysWinRight, cutoutRight); - return leftInset + mStackScroller.getRight() + rightInset - realDisplaySize; + return leftInset + mStackScrollerController.getRight() + rightInset - realDisplaySize; } public void updatePanelTranslation() { float newTranslation; - if (mStackScroller.isLayoutRtl()) { + if (mStackScrollerController.isLayoutRtl()) { newTranslation = getRtlTranslation(); } else { - newTranslation = mStackScroller.getLeft(); + newTranslation = mStackScrollerController.getLeft(); } - newTranslation += mStackScroller.getTranslationX(); + newTranslation += mStackScrollerController.getTranslationX(); mHeadsUpStatusBarView.setPanelTranslation(newTranslation); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 1d82e0808332..8092cb910b07 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -31,8 +31,8 @@ import com.android.systemui.R; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -58,6 +58,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, private final NotificationGroupManager mGroupManager; private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>(); private final int mAutoHeadsUpNotificationDecay; + // TODO (b/162832756): remove visual stability manager when migrating to new pipeline private VisualStabilityManager mVisualStabilityManager; private boolean mReleaseOnExpandFinish; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt index 0827511cac34..242bd0a29d2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -21,6 +21,7 @@ import android.content.pm.PackageManager import android.hardware.biometrics.BiometricSourceType import android.provider.Settings import com.android.systemui.Dumpable +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.NotificationLockscreenUserManager @@ -30,9 +31,8 @@ import com.android.systemui.tuner.TunerService import java.io.FileDescriptor import java.io.PrintWriter import javax.inject.Inject -import javax.inject.Singleton -@Singleton +@SysUISingleton open class KeyguardBypassController : Dumpable { private val mKeyguardStateController: KeyguardStateController diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java index 834d2a5ae4a0..c0181f448cc1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java @@ -18,16 +18,16 @@ package com.android.systemui.statusbar.phone; import android.util.Log; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import javax.inject.Inject; -import javax.inject.Singleton; /** * Executes actions that require the screen to be unlocked. Delegates the actual handling to an * implementation passed via {@link #setDismissHandler}. */ -@Singleton +@SysUISingleton public class KeyguardDismissUtil implements KeyguardDismissHandler { private static final String TAG = "KeyguardDismissUtil"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java index e763496da859..817b86bf643e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java @@ -21,14 +21,14 @@ import android.service.notification.StatusBarNotification; import android.util.Log; import com.android.systemui.Dependency; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import javax.inject.Inject; -import javax.inject.Singleton; -@Singleton +@SysUISingleton public class KeyguardEnvironmentImpl implements KeyguardEnvironment { private static final String TAG = "KeyguardEnvironmentImpl"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java index 3e5eb5fba8f2..24c902151d7c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java @@ -32,6 +32,8 @@ import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.internal.view.AppearanceRegion; import com.android.systemui.Dumpable; import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.policy.BatteryController; @@ -40,12 +42,11 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controls how light status bar flag applies to the icons. */ -@Singleton +@SysUISingleton public class LightBarController implements BatteryController.BatteryStateChangeCallback, Dumpable { private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f; @@ -125,7 +126,7 @@ public class LightBarController implements BatteryController.BatteryStateChangeC updateStatus(); } - void onNavigationBarAppearanceChanged(@Appearance int appearance, boolean nbModeChanged, + public void onNavigationBarAppearanceChanged(@Appearance int appearance, boolean nbModeChanged, int navigationBarMode, boolean navbarColorManagedByIme) { int diff = appearance ^ mAppearance; if ((diff & APPEARANCE_LIGHT_NAVIGATION_BARS) != 0 || nbModeChanged) { @@ -144,7 +145,7 @@ public class LightBarController implements BatteryController.BatteryStateChangeC mNavbarColorManagedByIme = navbarColorManagedByIme; } - void onNavigationBarModeChanged(int newBarMode) { + public void onNavigationBarModeChanged(int newBarMode) { mHasLightNavigationBar = isLight(mAppearance, newBarMode, APPEARANCE_LIGHT_NAVIGATION_BARS); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java index 8e192c5bf17d..d27a3d53c0a2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java @@ -29,13 +29,13 @@ import android.view.animation.AccelerateInterpolator; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.view.AppearanceRegion; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import javax.inject.Inject; -import javax.inject.Singleton; /** * Apps can request a low profile mode {@link View.SYSTEM_UI_FLAG_LOW_PROFILE} @@ -45,7 +45,7 @@ import javax.inject.Singleton; * This controller shows and hides the notification dot in the status bar to indicate * whether there are notifications when the device is in {@link View.SYSTEM_UI_FLAG_LOW_PROFILE}. */ -@Singleton +@SysUISingleton public class LightsOutNotifController { private final CommandQueue mCommandQueue; private final NotificationEntryManager mEntryManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java index 0d6597f1b11b..094ebb9ef0a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java @@ -27,15 +27,15 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.EventLogConstants; import com.android.systemui.EventLogTags; +import com.android.systemui.dagger.SysUISingleton; import javax.inject.Inject; -import javax.inject.Singleton; /** * Wrapper that emits both new- and old-style gesture logs. * TODO: delete this once the old logs are no longer needed. */ -@Singleton +@SysUISingleton public class LockscreenGestureLogger { /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java index 1dc0f070835b..11ceedf79227 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java @@ -37,6 +37,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -54,10 +55,9 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import java.util.Optional; import javax.inject.Inject; -import javax.inject.Singleton; /** Controls the {@link LockIcon} in the lockscreen. */ -@Singleton +@SysUISingleton public class LockscreenLockIconController { private final LockscreenGestureLogger mLockscreenGestureLogger; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java index 04211dff53b6..a6811c6b8ba4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java @@ -42,6 +42,7 @@ import androidx.annotation.NonNull; import com.android.internal.util.IndentingPrintWriter; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.NotificationMediaManager; @@ -53,12 +54,11 @@ import java.io.PrintWriter; import java.util.Objects; import javax.inject.Inject; -import javax.inject.Singleton; /** * Manages the lockscreen wallpaper. */ -@Singleton +@SysUISingleton public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implements Runnable, Dumpable { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java index 07e9f944b802..94d1bf4be806 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java @@ -24,18 +24,20 @@ import android.content.pm.UserInfo; import android.os.UserHandle; import android.os.UserManager; +import androidx.annotation.NonNull; + import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** */ -@Singleton +@SysUISingleton public class ManagedProfileControllerImpl implements ManagedProfileController { private final List<Callback> mCallbacks = new ArrayList<>(); @@ -57,7 +59,8 @@ public class ManagedProfileControllerImpl implements ManagedProfileController { mProfiles = new LinkedList<UserInfo>(); } - public void addCallback(Callback callback) { + @Override + public void addCallback(@NonNull Callback callback) { mCallbacks.add(callback); if (mCallbacks.size() == 1) { setListening(true); @@ -65,7 +68,8 @@ public class ManagedProfileControllerImpl implements ManagedProfileController { callback.onManagedProfileChanged(); } - public void removeCallback(Callback callback) { + @Override + public void removeCallback(@NonNull Callback callback) { if (mCallbacks.remove(callback) && mCallbacks.size() == 0) { setListening(false); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java index 80785db6df3e..c44c59c02810 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java @@ -23,6 +23,7 @@ import android.util.Log; import com.android.systemui.Dependency; import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.StatusBarState; @@ -40,14 +41,13 @@ import java.util.Map; import java.util.Objects; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; /** * A class to handle notifications and their corresponding groups. */ -@Singleton +@SysUISingleton public class NotificationGroupManager implements OnHeadsUpChangedListener, StateListener { private static final String TAG = "NotificationGroupManager"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index 7bbe1c986249..bda35fb0a48e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -5,9 +5,9 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Rect; +import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.FrameLayout; import androidx.annotation.NonNull; @@ -20,6 +20,9 @@ import com.android.settingslib.Utils; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.demomode.DemoMode; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -31,20 +34,26 @@ import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.function.Function; +import javax.inject.Inject; + /** * A controller for the space in the status bar to the left of the system icons. This area is * normally reserved for notifications. */ -public class NotificationIconAreaController implements DarkReceiver, +@SysUISingleton +public class NotificationIconAreaController implements + DarkReceiver, StatusBarStateController.StateListener, - NotificationWakeUpCoordinator.WakeUpListener { + NotificationWakeUpCoordinator.WakeUpListener, + DemoMode { public static final String HIGH_PRIORITY = "high_priority"; private static final long AOD_ICONS_APPEAR_DURATION = 200; @@ -57,13 +66,14 @@ public class NotificationIconAreaController implements DarkReceiver, private final KeyguardBypassController mBypassController; private final DozeParameters mDozeParameters; private final BubbleController mBubbleController; + private final StatusBarWindowController mStatusBarWindowController; private int mIconSize; private int mIconHPadding; private int mIconTint = Color.WHITE; private int mCenteredIconTint = Color.WHITE; - private StatusBar mStatusBar; + private List<ListEntry> mNotificationEntries = List.of(); protected View mNotificationIconArea; private NotificationIconContainer mNotificationIcons; private NotificationIconContainer mShelfIcons; @@ -72,8 +82,10 @@ public class NotificationIconAreaController implements DarkReceiver, private NotificationIconContainer mAodIcons; private StatusBarIconView mCenteredIconView; private final Rect mTintArea = new Rect(); - private ViewGroup mNotificationScrollLayout; private Context mContext; + + private final DemoModeController mDemoModeController; + private int mAodIconAppearTranslation; private boolean mAnimationsEnabled; @@ -88,24 +100,24 @@ public class NotificationIconAreaController implements DarkReceiver, new NotificationListener.NotificationSettingsListener() { @Override public void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) { - mShowLowPriority = !hideSilentStatusIcons; - if (mNotificationScrollLayout != null) { - updateStatusBarIcons(); - } + mShowLowPriority = !hideSilentStatusIcons; + updateStatusBarIcons(); } }; + @Inject public NotificationIconAreaController( Context context, - StatusBar statusBar, StatusBarStateController statusBarStateController, NotificationWakeUpCoordinator wakeUpCoordinator, KeyguardBypassController keyguardBypassController, NotificationMediaManager notificationMediaManager, NotificationListener notificationListener, DozeParameters dozeParameters, - BubbleController bubbleController) { - mStatusBar = statusBar; + BubbleController bubbleController, + DemoModeController demoModeController, + DarkIconDispatcher darkIconDispatcher, + StatusBarWindowController statusBarWindowController) { mContrastColorUtil = ContrastColorUtil.getInstance(context); mContext = context; mStatusBarStateController = statusBarStateController; @@ -116,10 +128,14 @@ public class NotificationIconAreaController implements DarkReceiver, wakeUpCoordinator.addListener(this); mBypassController = keyguardBypassController; mBubbleController = bubbleController; + mDemoModeController = demoModeController; + mDemoModeController.addCallback(this); + mStatusBarWindowController = statusBarWindowController; notificationListener.addNotificationSettingsListener(mSettingsListener); initializeNotificationAreaViews(context); reloadAodColor(); + darkIconDispatcher.addDarkReceiver(this); } protected View inflateIconArea(LayoutInflater inflater) { @@ -136,22 +152,21 @@ public class NotificationIconAreaController implements DarkReceiver, mNotificationIconArea = inflateIconArea(layoutInflater); mNotificationIcons = mNotificationIconArea.findViewById(R.id.notificationIcons); - mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout(); - mCenteredIconArea = layoutInflater.inflate(R.layout.center_icon_area, null); mCenteredIcon = mCenteredIconArea.findViewById(R.id.centeredIcon); - - initAodIcons(); } - public void initAodIcons() { + /** + * Called by the StatusBar. The StatusBar passes the NotificationIconContainer which holds + * the aod icons. + */ + void setupAodIcons(@NonNull NotificationIconContainer aodIcons) { boolean changed = mAodIcons != null; if (changed) { mAodIcons.setAnimationsEnabled(false); mAodIcons.removeAllViews(); } - mAodIcons = mStatusBar.getNotificationShadeWindowView().findViewById( - R.id.clock_notification_icon_container); + mAodIcons = aodIcons; mAodIcons.setOnLockScreen(true); updateAodIconsVisibility(false /* animate */); updateAnimations(); @@ -189,7 +204,7 @@ public class NotificationIconAreaController implements DarkReceiver, @NonNull private FrameLayout.LayoutParams generateIconLayoutParams() { return new FrameLayout.LayoutParams( - mIconSize + 2 * mIconHPadding, getHeight()); + mIconSize + 2 * mIconHPadding, mStatusBarWindowController.getStatusBarHeight()); } private void reloadDimens(Context context) { @@ -228,29 +243,17 @@ public class NotificationIconAreaController implements DarkReceiver, mTintArea.set(tintArea); } - if (mNotificationIconArea != null) { - if (DarkIconDispatcher.isInArea(tintArea, mNotificationIconArea)) { - mIconTint = iconTint; - } - } else { + if (DarkIconDispatcher.isInArea(tintArea, mNotificationIconArea)) { mIconTint = iconTint; } - if (mCenteredIconArea != null) { - if (DarkIconDispatcher.isInArea(tintArea, mCenteredIconArea)) { - mCenteredIconTint = iconTint; - } - } else { + if (DarkIconDispatcher.isInArea(tintArea, mCenteredIconArea)) { mCenteredIconTint = iconTint; } applyNotificationIconsTint(); } - protected int getHeight() { - return mStatusBar.getStatusBarHeight(); - } - protected boolean shouldShowNotificationIcon(NotificationEntry entry, boolean showAmbient, boolean showLowPriority, boolean hideDismissed, boolean hideRepliedMessages, boolean hideCurrentMedia, boolean hideCenteredIcon, @@ -300,11 +303,15 @@ public class NotificationIconAreaController implements DarkReceiver, } return true; } - /** * Updates the notifications with the given list of notifications to display. */ - public void updateNotificationIcons() { + public void updateNotificationIcons(List<ListEntry> entries) { + mNotificationEntries = entries; + updateNotificationIcons(); + } + + private void updateNotificationIcons() { updateStatusBarIcons(); updateShelfIcons(); updateCenterIcon(); @@ -381,18 +388,15 @@ public class NotificationIconAreaController implements DarkReceiver, NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority, boolean hideDismissed, boolean hideRepliedMessages, boolean hideCurrentMedia, boolean hideCenteredIcon, boolean hidePulsing, boolean onlyShowCenteredIcon) { - ArrayList<StatusBarIconView> toShow = new ArrayList<>( - mNotificationScrollLayout.getChildCount()); - + ArrayList<StatusBarIconView> toShow = new ArrayList<>(mNotificationEntries.size()); // Filter out ambient notifications and notification children. - for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) { - View view = mNotificationScrollLayout.getChildAt(i); - if (view instanceof ExpandableNotificationRow) { - NotificationEntry ent = ((ExpandableNotificationRow) view).getEntry(); - if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed, + for (int i = 0; i < mNotificationEntries.size(); i++) { + NotificationEntry entry = mNotificationEntries.get(i).getRepresentativeEntry(); + if (entry != null && entry.getRow() != null) { + if (shouldShowNotificationIcon(entry, showAmbient, showLowPriority, hideDismissed, hideRepliedMessages, hideCurrentMedia, hideCenteredIcon, hidePulsing, onlyShowCenteredIcon)) { - StatusBarIconView iconView = function.apply(ent); + StatusBarIconView iconView = function.apply(entry); if (iconView != null) { toShow.add(iconView); } @@ -597,13 +601,16 @@ public class NotificationIconAreaController implements DarkReceiver, mAodIconTint = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor); } + private void updateAodIconColors() { - for (int i = 0; i < mAodIcons.getChildCount(); i++) { - final StatusBarIconView iv = (StatusBarIconView) mAodIcons.getChildAt(i); - if (iv.getWidth() != 0) { - updateTintForIcon(iv, mAodIconTint); - } else { - iv.executeOnLayout(() -> updateTintForIcon(iv, mAodIconTint)); + if (mAodIcons != null) { + for (int i = 0; i < mAodIcons.getChildCount(); i++) { + final StatusBarIconView iv = (StatusBarIconView) mAodIcons.getChildAt(i); + if (iv.getWidth() != 0) { + updateTintForIcon(iv, mAodIconTint); + } else { + iv.executeOnLayout(() -> updateTintForIcon(iv, mAodIconTint)); + } } } } @@ -666,4 +673,27 @@ public class NotificationIconAreaController implements DarkReceiver, } } } + + @Override + public List<String> demoCommands() { + ArrayList<String> commands = new ArrayList<>(); + commands.add(DemoMode.COMMAND_NOTIFICATIONS); + return commands; + } + + @Override + public void dispatchDemoCommand(String command, Bundle args) { + if (mNotificationIconArea != null) { + String visible = args.getString("visible"); + int vis = "false".equals(visible) ? View.INVISIBLE : View.VISIBLE; + mNotificationIconArea.setVisibility(vis); + } + } + + @Override + public void onDemoModeFinished() { + if (mNotificationIconArea != null) { + mNotificationIconArea.setVisibility(View.VISIBLE); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 6946346eee2f..5974a53fc86d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -100,12 +100,14 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; +import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; @@ -175,6 +177,8 @@ public class NotificationPanelViewController extends PanelViewController { private final ZenModeController mZenModeController; private final ConfigurationController mConfigurationController; private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder; + private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; + private final NotificationIconAreaController mNotificationIconAreaController; // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is // changed. @@ -265,7 +269,6 @@ public class NotificationPanelViewController extends PanelViewController { private KeyguardStatusView mKeyguardStatusView; private View mQsNavbarScrim; private NotificationsQuickSettingsContainer mNotificationContainerParent; - private NotificationStackScrollLayout mNotificationStackScroller; private boolean mAnimateNextPositionUpdate; private int mTrackingPointer; @@ -500,7 +503,9 @@ public class NotificationPanelViewController extends PanelViewController { MediaHierarchyManager mediaHierarchyManager, BiometricUnlockController biometricUnlockController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, - Provider<KeyguardClockSwitchController> keyguardClockSwitchControllerProvider) { + Provider<KeyguardClockSwitchController> keyguardClockSwitchControllerProvider, + NotificationStackScrollLayoutController notificationStackScrollLayoutController, + NotificationIconAreaController notificationIconAreaController) { super(view, falsingManager, dozeLog, keyguardStateController, (SysuiStatusBarStateController) statusBarStateController, vibratorHelper, latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager); @@ -513,6 +518,8 @@ public class NotificationPanelViewController extends PanelViewController { mMediaHierarchyManager = mediaHierarchyManager; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mKeyguardClockSwitchControllerProvider = keyguardClockSwitchControllerProvider; + mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; + mNotificationIconAreaController = notificationIconAreaController; mView.setWillNotDraw(!DEBUG); mInjectionInflationController = injectionInflationController; mFalsingManager = falsingManager; @@ -592,21 +599,26 @@ public class NotificationPanelViewController extends PanelViewController { keyguardClockSwitchController.setBigClockContainer(mBigClockContainer); mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent); - mNotificationStackScroller = mView.findViewById(R.id.notification_stack_scroller); - mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener); - mNotificationStackScroller.setOverscrollTopChangedListener(mOnOverscrollTopChangedListener); - mNotificationStackScroller.setOnEmptySpaceClickListener(mOnEmptySpaceClickListener); - addTrackingHeadsUpListener(mNotificationStackScroller::setTrackingHeadsUp); + NotificationStackScrollLayout stackScrollLayout = mView.findViewById( + R.id.notification_stack_scroller); + mNotificationStackScrollLayoutController.attach(stackScrollLayout); + mNotificationStackScrollLayoutController.setOnHeightChangedListener( + mOnHeightChangedListener); + mNotificationStackScrollLayoutController.setOverscrollTopChangedListener( + mOnOverscrollTopChangedListener); + mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener( + mOnEmptySpaceClickListener); + addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp); mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area); mQsNavbarScrim = mView.findViewById(R.id.qs_navbar_scrim); mLastOrientation = mResources.getConfiguration().orientation; initBottomArea(); - mWakeUpCoordinator.setStackScroller(mNotificationStackScroller); + mWakeUpCoordinator.setStackScroller(mNotificationStackScrollLayoutController); mQsFrame = mView.findViewById(R.id.qs_frame); mPulseExpansionHandler.setUp( - mNotificationStackScroller, mExpansionCallback, mShadeController); + mNotificationStackScrollLayoutController, mExpansionCallback, mShadeController); mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() { @Override public void onFullyHiddenChanged(boolean isFullyHidden) { @@ -690,11 +702,11 @@ public class NotificationPanelViewController extends PanelViewController { } int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width); - lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams(); + lp = mNotificationStackScrollLayoutController.getLayoutParams(); if (lp.width != panelWidth || lp.gravity != panelGravity) { lp.width = panelWidth; lp.gravity = panelGravity; - mNotificationStackScroller.setLayoutParams(lp); + mNotificationStackScrollLayoutController.setLayoutParams(lp); } } @@ -772,7 +784,7 @@ public class NotificationPanelViewController extends PanelViewController { private void setIsFullWidth(boolean isFullWidth) { mIsFullWidth = isFullWidth; - mNotificationStackScroller.setIsFullWidth(isFullWidth); + mNotificationStackScrollLayoutController.setIsFullWidth(isFullWidth); } private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) { @@ -806,7 +818,7 @@ public class NotificationPanelViewController extends PanelViewController { * showing. */ private void positionClockAndNotifications() { - boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending(); + boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending(); boolean animateClock = animate || mAnimateNextPositionUpdate; int stackScrollerPadding; if (mBarState != StatusBarState.KEYGUARD) { @@ -816,12 +828,12 @@ public class NotificationPanelViewController extends PanelViewController { int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); int clockPreferredY = mKeyguardStatusView.getClockPreferredY(totalHeight); boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); - final boolean - hasVisibleNotifications = - !bypassEnabled && mNotificationStackScroller.getVisibleNotificationCount() != 0; + final boolean hasVisibleNotifications = !bypassEnabled + && mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0; mKeyguardStatusView.setHasVisibleNotifications(hasVisibleNotifications); mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding, - mNotificationStackScroller.getIntrinsicContentHeight(), getExpandedFraction(), + mNotificationStackScrollLayoutController.getIntrinsicContentHeight(), + getExpandedFraction(), totalHeight, (int) (mKeyguardStatusView.getHeight() - mShelfHeight / 2.0f - mDarkIconSize / 2.0f), clockPreferredY, hasCustomClock(), hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount, @@ -835,7 +847,7 @@ public class NotificationPanelViewController extends PanelViewController { updateClock(); stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded; } - mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding); + mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding); mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX); mStackScrollerMeasuringPass++; @@ -867,29 +879,44 @@ public class NotificationPanelViewController extends PanelViewController { : shelf.getIntrinsicHeight() + notificationPadding; float availableSpace = - mNotificationStackScroller.getHeight() - minPadding - shelfSize - Math.max( - mIndicationBottomPadding, mAmbientIndicationBottomPadding) + mNotificationStackScrollLayoutController.getHeight() - minPadding - shelfSize + - Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding) - mKeyguardStatusView.getLogoutButtonHeight(); int count = 0; ExpandableView previousView = null; - for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) { - ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i); + for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) { + ExpandableView child = mNotificationStackScrollLayoutController.getChildAt(i); + if (!(child instanceof ExpandableNotificationRow)) { + continue; + } + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + boolean + suppressedSummary = + mGroupManager != null && mGroupManager.isSummaryOfSuppressedGroup( + row.getEntry().getSbn()); + if (suppressedSummary) { + continue; + } if (!canShowViewOnLockscreen(child)) { continue; } + if (row.isRemoved()) { + continue; + } availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */); availableSpace -= count == 0 ? 0 : notificationPadding; - availableSpace -= mNotificationStackScroller.calculateGapHeight(previousView, child, - count); + availableSpace -= mNotificationStackScrollLayoutController + .calculateGapHeight(previousView, child, count); previousView = child; if (availableSpace >= 0 && count < maximum) { count++; } else if (availableSpace > -shelfSize) { // if we are exactly the last view, then we can show us still! - for (int j = i + 1; j < mNotificationStackScroller.getChildCount(); j++) { - ExpandableView view = (ExpandableView) mNotificationStackScroller.getChildAt(j); - if (view instanceof ExpandableNotificationRow && - canShowViewOnLockscreen(view)) { + int childCount = mNotificationStackScrollLayoutController.getChildCount(); + for (int j = i + 1; j < childCount; j++) { + ExpandableView view = mNotificationStackScrollLayoutController.getChildAt(j); + if (view instanceof ExpandableNotificationRow + && canShowViewOnLockscreen(view)) { return count; } } @@ -954,7 +981,7 @@ public class NotificationPanelViewController extends PanelViewController { } public void animateToFullShade(long delay) { - mNotificationStackScroller.goToFullShade(delay); + mNotificationStackScrollLayoutController.goToFullShade(delay); mView.requestLayout(); mAnimateNextPositionUpdate = true; } @@ -980,9 +1007,9 @@ public class NotificationPanelViewController extends PanelViewController { } else { closeQs(); } - mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, animate, + mNotificationStackScrollLayoutController.setOverScrollAmount(0f, true /* onTop */, animate, !animate /* cancelAnimators */); - mNotificationStackScroller.resetScrollPosition(); + mNotificationStackScrollLayoutController.resetScrollPosition(); } @Override @@ -993,7 +1020,7 @@ public class NotificationPanelViewController extends PanelViewController { if (mQsExpanded) { mQsExpandImmediate = true; - mNotificationStackScroller.setShouldShowShelfOnly(true); + mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); } super.collapse(delayed, speedUpFactor); } @@ -1029,7 +1056,7 @@ public class NotificationPanelViewController extends PanelViewController { public void expandWithQs() { if (mQsExpansionEnabled) { mQsExpandImmediate = true; - mNotificationStackScroller.setShouldShowShelfOnly(true); + mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); } if (isFullyCollapsed()) { expand(true /* animate */); @@ -1090,7 +1117,7 @@ public class NotificationPanelViewController extends PanelViewController { onQsExpansionStarted(); mInitialHeightOnTouch = mQsExpansionHeight; mQsTracking = true; - mNotificationStackScroller.cancelLongPress(); + mNotificationStackScrollLayoutController.cancelLongPress(); } break; case MotionEvent.ACTION_POINTER_UP: @@ -1126,7 +1153,7 @@ public class NotificationPanelViewController extends PanelViewController { mInitialHeightOnTouch = mQsExpansionHeight; mInitialTouchY = y; mInitialTouchX = x; - mNotificationStackScroller.cancelLongPress(); + mNotificationStackScrollLayoutController.cancelLongPress(); return true; } break; @@ -1146,9 +1173,11 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected boolean isInContentBounds(float x, float y) { - float stackScrollerX = mNotificationStackScroller.getX(); - return !mNotificationStackScroller.isBelowLastNotification(x - stackScrollerX, y) - && stackScrollerX < x && x < stackScrollerX + mNotificationStackScroller.getWidth(); + float stackScrollerX = mNotificationStackScrollLayoutController.getX(); + return !mNotificationStackScrollLayoutController + .isBelowLastNotification(x - stackScrollerX, y) + && stackScrollerX < x + && x < stackScrollerX + mNotificationStackScrollLayoutController.getWidth(); } private void initDownStates(MotionEvent event) { @@ -1256,7 +1285,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected float getOpeningHeight() { - return mNotificationStackScroller.getOpeningHeight(); + return mNotificationStackScrollLayoutController.getOpeningHeight(); } @@ -1292,7 +1321,7 @@ public class NotificationPanelViewController extends PanelViewController { < mStatusBarMinHeight) { mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1); mQsExpandImmediate = true; - mNotificationStackScroller.setShouldShowShelfOnly(true); + mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); requestPanelHeightUpdate(); // Normally, we start listening when the panel is expanded, but here we need to start @@ -1304,7 +1333,7 @@ public class NotificationPanelViewController extends PanelViewController { private boolean isInQsArea(float x, float y) { return (x >= mQsFrame.getX() && x <= mQsFrame.getX() + mQsFrame.getWidth()) && ( - y <= mNotificationStackScroller.getBottomMostNotificationBottom() + y <= mNotificationStackScrollLayoutController.getBottomMostNotificationBottom() || y <= mQs.getView().getY() + mQs.getView().getHeight()); } @@ -1494,7 +1523,7 @@ public class NotificationPanelViewController extends PanelViewController { float height = mQsExpansionHeight - overscrollAmount; setQsExpansion(height); requestPanelHeightUpdate(); - mNotificationStackScroller.checkSnoozeLeavebehind(); + mNotificationStackScrollLayoutController.checkSnoozeLeavebehind(); // When expanding QS, let's authenticate the user if possible, // this will speed up notification actions. @@ -1667,8 +1696,8 @@ public class NotificationPanelViewController extends PanelViewController { } private void updateQsState() { - mNotificationStackScroller.setQsExpanded(mQsExpanded); - mNotificationStackScroller.setScrollingEnabled( + mNotificationStackScrollLayoutController.setQsExpanded(mQsExpanded); + mNotificationStackScrollLayoutController.setScrollingEnabled( mBarState != StatusBarState.KEYGUARD && (!mQsExpanded || mQsExpansionFromOverscroll)); updateEmptyShadeView(); @@ -1728,7 +1757,7 @@ public class NotificationPanelViewController extends PanelViewController { float qsExpansionFraction = getQsExpansionFraction(); mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation()); mMediaHierarchyManager.setQsExpansion(qsExpansionFraction); - mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction); + mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction); } private String determineAccessibilityPaneTitle() { @@ -1787,18 +1816,18 @@ public class NotificationPanelViewController extends PanelViewController { return mClockPositionResult.stackScrollerPadding; } int collapsedPosition = mHeadsUpInset; - if (!mNotificationStackScroller.isPulseExpanding()) { + if (!mNotificationStackScrollLayoutController.isPulseExpanding()) { return collapsedPosition; } else { int expandedPosition = mClockPositionResult.stackScrollerPadding; return (int) MathUtils.lerp(collapsedPosition, expandedPosition, - mNotificationStackScroller.calculateAppearFractionBypass()); + mNotificationStackScrollLayoutController.calculateAppearFractionBypass()); } } protected void requestScrollerTopPaddingUpdate(boolean animate) { - mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), animate); + mNotificationStackScrollLayoutController.updateTopPadding(calculateQsTopPadding(), animate); if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) { // update the position of the header updateQsExpansion(); @@ -1810,7 +1839,7 @@ public class NotificationPanelViewController extends PanelViewController { if (mQs != null) { mQs.setShowCollapsedOnKeyguard( mKeyguardShowing && mKeyguardBypassController.getBypassEnabled() - && mNotificationStackScroller.isPulseExpanding()); + && mNotificationStackScrollLayoutController.isPulseExpanding()); } } @@ -1906,7 +1935,7 @@ public class NotificationPanelViewController extends PanelViewController { public void onAnimationEnd(Animator animation) { mAnimatingQS = false; notifyExpandingFinished(); - mNotificationStackScroller.resetCheckSnoozeLeavebehind(); + mNotificationStackScrollLayoutController.resetCheckSnoozeLeavebehind(); mQsExpansionAnimator = null; if (onFinishRunnable != null) { onFinishRunnable.run(); @@ -1945,8 +1974,8 @@ public class NotificationPanelViewController extends PanelViewController { protected boolean canCollapsePanelOnTouch() { if (!isInSettings()) { return mBarState == StatusBarState.KEYGUARD - || mNotificationStackScroller.isScrolledToBottom() - || mIsPanelCollapseOnQQS; + || mIsPanelCollapseOnQQS + || mNotificationStackScrollLayoutController.isScrolledToBottom(); } else { return true; } @@ -1964,7 +1993,7 @@ public class NotificationPanelViewController extends PanelViewController { private int getMaxPanelHeightNonBypass() { int min = mStatusBarMinHeight; if (!(mBarState == StatusBarState.KEYGUARD) - && mNotificationStackScroller.getNotGoneChildCount() == 0) { + && mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0) { int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount()); min = Math.max(min, minHeight); } @@ -1990,7 +2019,7 @@ public class NotificationPanelViewController extends PanelViewController { int position = mClockPositionAlgorithm.getExpandedClockPosition() + mKeyguardStatusView.getHeight(); - if (mNotificationStackScroller.getVisibleNotificationCount() != 0) { + if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0) { position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f; } return position; @@ -2029,8 +2058,8 @@ public class NotificationPanelViewController extends PanelViewController { // minimum QS expansion + minStackHeight float panelHeightQsCollapsed = - mNotificationStackScroller.getIntrinsicPadding() - + mNotificationStackScroller.getLayoutMinHeight(); + mNotificationStackScrollLayoutController.getIntrinsicPadding() + + mNotificationStackScrollLayoutController.getLayoutMinHeight(); float panelHeightQsExpanded = calculatePanelHeightQsExpanded(); t = (expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded @@ -2062,16 +2091,16 @@ public class NotificationPanelViewController extends PanelViewController { } private int calculatePanelHeightShade() { - int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin(); - int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin; - maxHeight += mNotificationStackScroller.getTopPaddingOverflow(); + int emptyBottomMargin = mNotificationStackScrollLayoutController.getEmptyBottomMargin(); + int maxHeight = mNotificationStackScrollLayoutController.getHeight() - emptyBottomMargin; + maxHeight += mNotificationStackScrollLayoutController.getTopPaddingOverflow(); if (mBarState == StatusBarState.KEYGUARD) { int minKeyguardPanelBottom = mClockPositionAlgorithm.getExpandedClockPosition() + mKeyguardStatusView.getHeight() - + mNotificationStackScroller.getIntrinsicContentHeight(); + + mNotificationStackScrollLayoutController.getIntrinsicContentHeight(); return Math.max(maxHeight, minKeyguardPanelBottom); } else { return maxHeight; @@ -2081,15 +2110,16 @@ public class NotificationPanelViewController extends PanelViewController { private int calculatePanelHeightQsExpanded() { float notificationHeight = - mNotificationStackScroller.getHeight() - - mNotificationStackScroller.getEmptyBottomMargin() - - mNotificationStackScroller.getTopPadding(); + mNotificationStackScrollLayoutController.getHeight() + - mNotificationStackScrollLayoutController.getEmptyBottomMargin() + - mNotificationStackScrollLayoutController.getTopPadding(); // When only empty shade view is visible in QS collapsed state, simulate that we would have // it in expanded QS state as well so we don't run into troubles when fading the view in/out // and expanding/collapsing the whole panel from/to quick settings. - if (mNotificationStackScroller.getNotGoneChildCount() == 0 && mShowEmptyShadeView) { - notificationHeight = mNotificationStackScroller.getEmptyShadeViewHeight(); + if (mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0 + && mShowEmptyShadeView) { + notificationHeight = mNotificationStackScrollLayoutController.getEmptyShadeViewHeight(); } int maxQsHeight = mQsMaxExpansionHeight; @@ -2104,12 +2134,13 @@ public class NotificationPanelViewController extends PanelViewController { float totalHeight = Math.max(maxQsHeight, mBarState == StatusBarState.KEYGUARD ? mClockPositionResult.stackScrollerPadding : 0) + notificationHeight - + mNotificationStackScroller.getTopPaddingOverflow(); - if (totalHeight > mNotificationStackScroller.getHeight()) { + + mNotificationStackScrollLayoutController.getTopPaddingOverflow(); + if (totalHeight > mNotificationStackScrollLayoutController.getHeight()) { float fullyCollapsedHeight = - maxQsHeight + mNotificationStackScroller.getLayoutMinHeight(); - totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight()); + maxQsHeight + mNotificationStackScrollLayoutController.getLayoutMinHeight(); + totalHeight = Math.max(fullyCollapsedHeight, + mNotificationStackScrollLayoutController.getHeight()); } return (int) totalHeight; } @@ -2124,7 +2155,7 @@ public class NotificationPanelViewController extends PanelViewController { && !mKeyguardBypassController.getBypassEnabled()) { alpha *= mClockPositionResult.clockAlpha; } - mNotificationStackScroller.setAlpha(alpha); + mNotificationStackScrollLayoutController.setAlpha(alpha); } private float getFadeoutAlpha() { @@ -2140,7 +2171,8 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected float getOverExpansionAmount() { - float result = mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */); + float result = mNotificationStackScrollLayoutController + .getCurrentOverScrollAmount(true /* top */); if (isNaN(result)) { Log.wtf(TAG, "OverExpansionAmount is NaN!"); } @@ -2150,7 +2182,8 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected float getOverExpansionPixels() { - return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */); + return mNotificationStackScrollLayoutController + .getCurrentOverScrolledPixels(true /* top */); } /** @@ -2167,17 +2200,19 @@ public class NotificationPanelViewController extends PanelViewController { if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) { return -mQs.getQsMinExpansionHeight(); } - float appearAmount = mNotificationStackScroller.calculateAppearFraction(mExpandedHeight); + float appearAmount = mNotificationStackScrollLayoutController + .calculateAppearFraction(mExpandedHeight); float startHeight = -mQsExpansionHeight; if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard() - && mNotificationStackScroller.isPulseExpanding()) { + && mNotificationStackScrollLayoutController.isPulseExpanding()) { if (!mPulseExpansionHandler.isExpanding() && !mPulseExpansionHandler.getLeavingLockscreen()) { // If we aborted the expansion we need to make sure the header doesn't reappear // again after the header has animated away appearAmount = 0; } else { - appearAmount = mNotificationStackScroller.calculateAppearFractionBypass(); + appearAmount = mNotificationStackScrollLayoutController + .calculateAppearFractionBypass(); } startHeight = -mQs.getQsMinExpansionHeight(); } @@ -2266,7 +2301,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected void onExpandingStarted() { super.onExpandingStarted(); - mNotificationStackScroller.onExpansionStarted(); + mNotificationStackScrollLayoutController.onExpansionStarted(); mIsExpanding = true; mQsExpandedWhenExpandingStarted = mQsFullyExpanded; mMediaHierarchyManager.setCollapsingShadeFromQS(mQsExpandedWhenExpandingStarted && @@ -2284,7 +2319,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected void onExpandingFinished() { super.onExpandingFinished(); - mNotificationStackScroller.onExpansionStopped(); + mNotificationStackScrollLayoutController.onExpansionStopped(); mHeadsUpManager.onExpandingFinished(); mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed()); mIsExpanding = false; @@ -2310,7 +2345,7 @@ public class NotificationPanelViewController extends PanelViewController { setListening(true); } mQsExpandImmediate = false; - mNotificationStackScroller.setShouldShowShelfOnly(false); + mNotificationStackScrollLayoutController.setShouldShowShelfOnly(false); mTwoFingerQsExpandPossible = false; notifyListenersTrackingHeadsUp(null); mExpandingFromHeadsUp = false; @@ -2342,15 +2377,16 @@ public class NotificationPanelViewController extends PanelViewController { return; } if (mBarState != StatusBarState.KEYGUARD) { - mNotificationStackScroller.setOnHeightChangedListener(null); + mNotificationStackScrollLayoutController.setOnHeightChangedListener(null); if (isPixels) { - mNotificationStackScroller.setOverScrolledPixels(overExpansion, true /* onTop */, - false /* animate */); + mNotificationStackScrollLayoutController.setOverScrolledPixels( + overExpansion, true /* onTop */, false /* animate */); } else { - mNotificationStackScroller.setOverScrollAmount(overExpansion, true /* onTop */, - false /* animate */); + mNotificationStackScrollLayoutController.setOverScrollAmount( + overExpansion, true /* onTop */, false /* animate */); } - mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener); + mNotificationStackScrollLayoutController + .setOnHeightChangedListener(mOnHeightChangedListener); } } @@ -2360,12 +2396,12 @@ public class NotificationPanelViewController extends PanelViewController { super.onTrackingStarted(); if (mQsFullyExpanded) { mQsExpandImmediate = true; - mNotificationStackScroller.setShouldShowShelfOnly(true); + mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); } if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) { mAffordanceHelper.animateHideLeftRightIcon(); } - mNotificationStackScroller.onPanelTrackingStarted(); + mNotificationStackScrollLayoutController.onPanelTrackingStarted(); } @Override @@ -2373,10 +2409,10 @@ public class NotificationPanelViewController extends PanelViewController { mFalsingManager.onTrackingStopped(); super.onTrackingStopped(expand); if (expand) { - mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */, + mNotificationStackScrollLayoutController.setOverScrolledPixels(0.0f, true /* onTop */, true /* animate */); } - mNotificationStackScroller.onPanelTrackingStopped(); + mNotificationStackScrollLayoutController.onPanelTrackingStopped(); if (expand && (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED)) { if (!mHintAnimationRunning) { @@ -2386,7 +2422,8 @@ public class NotificationPanelViewController extends PanelViewController { } private void updateMaxHeadsUpTranslation() { - mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mNavigationBarBottomHeight); + mNotificationStackScrollLayoutController.setHeadsUpBoundaries( + getHeight(), mNavigationBarBottomHeight); } @Override @@ -2402,19 +2439,19 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected void onUnlockHintFinished() { super.onUnlockHintFinished(); - mNotificationStackScroller.setUnlockHintRunning(false); + mNotificationStackScrollLayoutController.setUnlockHintRunning(false); } @Override protected void onUnlockHintStarted() { super.onUnlockHintStarted(); - mNotificationStackScroller.setUnlockHintRunning(true); + mNotificationStackScrollLayoutController.setUnlockHintRunning(true); } @Override protected float getPeekHeight() { - if (mNotificationStackScroller.getNotGoneChildCount() > 0) { - return mNotificationStackScroller.getPeekHeight(); + if (mNotificationStackScrollLayoutController.getNotGoneChildCount() > 0) { + return mNotificationStackScrollLayoutController.getPeekHeight(); } else { return mQsMinExpansionHeight; } @@ -2428,7 +2465,8 @@ public class NotificationPanelViewController extends PanelViewController { } // Let's make sure we're not appearing but the animation will end below the appear. // Otherwise quick settings would jump at the end of the animation. - float fraction = mNotificationStackScroller.calculateAppearFraction(targetHeight); + float fraction = mNotificationStackScrollLayoutController + .calculateAppearFraction(targetHeight); return fraction >= 1.0f; } @@ -2440,18 +2478,19 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected boolean fullyExpandedClearAllVisible() { - return mNotificationStackScroller.isFooterViewNotGone() - && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate; + return mNotificationStackScrollLayoutController.isFooterViewNotGone() + && mNotificationStackScrollLayoutController.isScrolledToBottom() + && !mQsExpandImmediate; } @Override protected boolean isClearAllVisible() { - return mNotificationStackScroller.isFooterViewContentVisible(); + return mNotificationStackScrollLayoutController.isFooterViewContentVisible(); } @Override protected int getClearAllHeightWithPadding() { - return mNotificationStackScroller.getFooterViewHeightWithPadding(); + return mNotificationStackScrollLayoutController.getFooterViewHeightWithPadding(); } @Override @@ -2502,7 +2541,8 @@ public class NotificationPanelViewController extends PanelViewController { private void updateEmptyShadeView() { // Hide "No notifications" in QS. - mNotificationStackScroller.updateEmptyShadeView(mShowEmptyShadeView && !mQsExpanded); + mNotificationStackScrollLayoutController.updateEmptyShadeView( + mShowEmptyShadeView && !mQsExpanded); } public void setQsScrimEnabled(boolean qsScrimEnabled) { @@ -2593,7 +2633,7 @@ public class NotificationPanelViewController extends PanelViewController { public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { mHeadsUpAnimatingAway = headsUpAnimatingAway; - mNotificationStackScroller.setHeadsUpAnimatingAway(headsUpAnimatingAway); + mNotificationStackScrollLayoutController.setHeadsUpAnimatingAway(headsUpAnimatingAway); updateHeadsUpVisibility(); } @@ -2605,7 +2645,7 @@ public class NotificationPanelViewController extends PanelViewController { public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { super.setHeadsUpManager(headsUpManager); mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, - mNotificationStackScroller.getHeadsUpCallback(), + mNotificationStackScrollLayoutController.getHeadsUpCallback(), NotificationPanelViewController.this); } @@ -2627,7 +2667,7 @@ public class NotificationPanelViewController extends PanelViewController { private void setClosingWithAlphaFadeout(boolean closing) { mClosingWithAlphaFadeOut = closing; - mNotificationStackScroller.forceNoOverlappingRendering(closing); + mNotificationStackScrollLayoutController.forceNoOverlappingRendering(closing); } /** @@ -2637,22 +2677,24 @@ public class NotificationPanelViewController extends PanelViewController { * @param x the x-coordinate the touch event */ protected void updateVerticalPanelPosition(float x) { - if (mNotificationStackScroller.getWidth() * 1.75f > mView.getWidth()) { + if (mNotificationStackScrollLayoutController.getWidth() * 1.75f > mView.getWidth()) { resetHorizontalPanelPosition(); return; } - float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2; + float leftMost = mPositionMinSideMargin + + mNotificationStackScrollLayoutController.getWidth() / 2; float rightMost = mView.getWidth() - mPositionMinSideMargin - - mNotificationStackScroller.getWidth() / 2; - if (Math.abs(x - mView.getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) { + - mNotificationStackScrollLayoutController.getWidth() / 2; + if (Math.abs(x - mView.getWidth() / 2) + < mNotificationStackScrollLayoutController.getWidth() / 4) { x = mView.getWidth() / 2; } x = Math.min(rightMost, Math.max(leftMost, x)); float - center = - mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth() / 2; + center = mNotificationStackScrollLayoutController.getLeft() + + mNotificationStackScrollLayoutController.getWidth() / 2; setHorizontalPanelTranslation(x - center); } @@ -2661,7 +2703,7 @@ public class NotificationPanelViewController extends PanelViewController { } protected void setHorizontalPanelTranslation(float translation) { - mNotificationStackScroller.setTranslationX(translation); + mNotificationStackScrollLayoutController.setTranslationX(translation); mQsFrame.setTranslationX(translation); int size = mVerticalTranslationListener.size(); for (int i = 0; i < size; i++) { @@ -2671,13 +2713,14 @@ public class NotificationPanelViewController extends PanelViewController { protected void updateExpandedHeight(float expandedHeight) { if (mTracking) { - mNotificationStackScroller.setExpandingVelocity(getCurrentExpandVelocity()); + mNotificationStackScrollLayoutController + .setExpandingVelocity(getCurrentExpandVelocity()); } if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) { // The expandedHeight is always the full panel Height when bypassing expandedHeight = getMaxPanelHeightNonBypass(); } - mNotificationStackScroller.setExpandedHeight(expandedHeight); + mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight); updateKeyguardBottomAreaAlpha(); updateBigClockAlpha(); updateStatusBarIcons(); @@ -2837,7 +2880,7 @@ public class NotificationPanelViewController extends PanelViewController { mHeightListener.onQsHeightChanged(); } }); - mNotificationStackScroller.setQsContainer((ViewGroup) mQs.getView()); + mNotificationStackScrollLayoutController.setQsContainer((ViewGroup) mQs.getView()); if (mQs instanceof QSFragment) { mKeyguardStatusBar.setQSPanel(((QSFragment) mQs).getQsPanel()); } @@ -2861,7 +2904,7 @@ public class NotificationPanelViewController extends PanelViewController { if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) { mAffordanceHelper.reset(false /* animate */); } - mNotificationStackScroller.setAnimationsEnabled(!disabled); + mNotificationStackScrollLayoutController.setAnimationsEnabled(!disabled); } /** @@ -2875,7 +2918,7 @@ public class NotificationPanelViewController extends PanelViewController { if (dozing == mDozing) return; mView.setDozing(dozing); mDozing = dozing; - mNotificationStackScroller.setDozing(mDozing, animate, wakeUpTouchLocation); + mNotificationStackScrollLayoutController.setDozing(mDozing, animate, wakeUpTouchLocation); mKeyguardBottomArea.setDozing(mDozing, animate); if (dozing) { @@ -2903,7 +2946,7 @@ public class NotificationPanelViewController extends PanelViewController { if (!mPulsing && !mDozing) { mAnimateNextPositionUpdate = false; } - mNotificationStackScroller.setPulsing(pulsing, animatePulse); + mNotificationStackScrollLayoutController.setPulsing(pulsing, animatePulse); mKeyguardStatusView.setPulsing(pulsing); } @@ -3008,7 +3051,7 @@ public class NotificationPanelViewController extends PanelViewController { } public boolean hasActiveClearableNotifications() { - return mNotificationStackScroller.hasActiveClearableNotifications(ROWS_ALL); + return mNotificationStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL); } private void updateShowEmptyShadeView() { @@ -3019,53 +3062,69 @@ public class NotificationPanelViewController extends PanelViewController { } public RemoteInputController.Delegate createRemoteInputDelegate() { - return mNotificationStackScroller.createDelegate(); + return mNotificationStackScrollLayoutController.createDelegate(); } void updateNotificationViews(String reason) { - mNotificationStackScroller.updateSectionBoundaries(reason); - mNotificationStackScroller.updateSpeedBumpIndex(); - mNotificationStackScroller.updateFooter(); + mNotificationStackScrollLayoutController.updateSectionBoundaries(reason); + mNotificationStackScrollLayoutController.updateSpeedBumpIndex(); + mNotificationStackScrollLayoutController.updateFooter(); updateShowEmptyShadeView(); - mNotificationStackScroller.updateIconAreaViews(); + mNotificationIconAreaController.updateNotificationIcons(createVisibleEntriesList()); + } + + private List<ListEntry> createVisibleEntriesList() { + List<ListEntry> entries = new ArrayList<>( + mNotificationStackScrollLayoutController.getChildCount()); + for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) { + View view = mNotificationStackScrollLayoutController.getChildAt(i); + if (view instanceof ExpandableNotificationRow) { + entries.add(((ExpandableNotificationRow) view).getEntry()); + } + } + return entries; } public void onUpdateRowStates() { - mNotificationStackScroller.onUpdateRowStates(); + mNotificationStackScrollLayoutController.onUpdateRowStates(); } public boolean hasPulsingNotifications() { - return mNotificationStackScroller.hasPulsingNotifications(); + return mNotificationStackScrollLayoutController + .getNotificationListContainer().hasPulsingNotifications(); } public ActivatableNotificationView getActivatedChild() { - return mNotificationStackScroller.getActivatedChild(); + return mNotificationStackScrollLayoutController.getActivatedChild(); } public void setActivatedChild(ActivatableNotificationView o) { - mNotificationStackScroller.setActivatedChild(o); + mNotificationStackScrollLayoutController.setActivatedChild(o); } public void runAfterAnimationFinished(Runnable r) { - mNotificationStackScroller.runAfterAnimationFinished(r); + mNotificationStackScrollLayoutController.runAfterAnimationFinished(r); } public void setScrollingEnabled(boolean b) { - mNotificationStackScroller.setScrollingEnabled(b); + mNotificationStackScrollLayoutController.setScrollingEnabled(b); } - public void initDependencies(StatusBar statusBar, NotificationGroupManager groupManager, + /** + * Initialize objects instead of injecting to avoid circular dependencies. + */ + public void initDependencies( + StatusBar statusBar, + NotificationGroupManager groupManager, NotificationShelfController notificationShelfController, - NotificationIconAreaController notificationIconAreaController, ScrimController scrimController) { setStatusBar(statusBar); setGroupManager(mGroupManager); - mNotificationStackScroller.setNotificationPanelController(this); - mNotificationStackScroller.setIconAreaController(notificationIconAreaController); - mNotificationStackScroller.setStatusBar(statusBar); - mNotificationStackScroller.setGroupManager(groupManager); - mNotificationStackScroller.setShelfController(notificationShelfController); - mNotificationStackScroller.setScrimController(scrimController); + mNotificationStackScrollLayoutController.setNotificationPanelController(this); + mNotificationStackScrollLayoutController.setStatusBar(statusBar); + mNotificationStackScrollLayoutController.setGroupManager(groupManager); + mNotificationStackScrollLayoutController.setShelfController(notificationShelfController); + mNotificationStackScrollLayoutController.setScrimController(scrimController); updateShowEmptyShadeView(); mNotificationShelfController = notificationShelfController; } @@ -3219,6 +3278,10 @@ public class NotificationPanelViewController extends PanelViewController { return new OnConfigurationChangedListener(); } + public NotificationStackScrollLayoutController getNotificationStackScrollLayoutController() { + return mNotificationStackScrollLayoutController; + } + private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener { @Override public void onHeightChanged(ExpandableView view, boolean needsAnimation) { @@ -3231,7 +3294,8 @@ public class NotificationPanelViewController extends PanelViewController { if (needsAnimation && mInterpolatedDarkAmount == 0) { mAnimateNextPositionUpdate = true; } - ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone(); + ExpandableView firstChildNotGone = + mNotificationStackScrollLayoutController.getFirstChildNotGone(); ExpandableNotificationRow firstRow = firstChildNotGone instanceof ExpandableNotificationRow @@ -3457,13 +3521,13 @@ public class NotificationPanelViewController extends PanelViewController { private class MyOnHeadsUpChangedListener implements OnHeadsUpChangedListener { @Override public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) { - mNotificationStackScroller.setInHeadsUpPinnedMode(inPinnedMode); + mNotificationStackScrollLayoutController.setInHeadsUpPinnedMode(inPinnedMode); if (inPinnedMode) { mHeadsUpExistenceChangedRunnable.run(); updateNotificationTranslucency(); } else { setHeadsUpAnimatingAway(true); - mNotificationStackScroller.runAfterAnimationFinished( + mNotificationStackScrollLayoutController.runAfterAnimationFinished( mHeadsUpExistenceChangedRunnable); } updateGestureExclusionRect(); @@ -3475,8 +3539,8 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onHeadsUpPinned(NotificationEntry entry) { if (!isOnKeyguard()) { - mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), - true); + mNotificationStackScrollLayoutController.generateHeadsUpAnimation( + entry.getHeadsUpAnimationView(), true); } } @@ -3488,7 +3552,7 @@ public class NotificationPanelViewController extends PanelViewController { // notification // will stick to the top without any interaction. if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) { - mNotificationStackScroller.generateHeadsUpAnimation( + mNotificationStackScrollLayoutController.generateHeadsUpAnimation( entry.getHeadsUpAnimationView(), false); entry.setHeadsUpIsVisible(); } @@ -3496,7 +3560,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { - mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp); + mNotificationStackScrollLayoutController.generateHeadsUpAnimation(entry, isHeadsUp); } } @@ -3511,7 +3575,7 @@ public class NotificationPanelViewController extends PanelViewController { if (mAccessibilityManager.isEnabled()) { mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); } - mNotificationStackScroller.setMaxTopPadding( + mNotificationStackScrollLayoutController.setMaxTopPadding( mQsMaxExpansionHeight + mQsNotificationTopPadding); } } @@ -3573,7 +3637,7 @@ public class NotificationPanelViewController extends PanelViewController { } else if (oldState == StatusBarState.SHADE_LOCKED && statusBarState == StatusBarState.KEYGUARD) { animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); - mNotificationStackScroller.resetScrollPosition(); + mNotificationStackScrollLayoutController.resetScrollPosition(); // Only animate header if the header is visible. If not, it will partially // animate out // the top of QS @@ -3649,7 +3713,7 @@ public class NotificationPanelViewController extends PanelViewController { int oldTop, int oldRight, int oldBottom) { DejankUtils.startDetectingBlockingIpcs("NVP#onLayout"); super.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom); - setIsFullWidth(mNotificationStackScroller.getWidth() == mView.getWidth()); + setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth()); // Update Clock Pivot mKeyguardStatusView.setPivotX(mView.getWidth() / 2); @@ -3665,7 +3729,7 @@ public class NotificationPanelViewController extends PanelViewController { mQsExpansionHeight = mQsMinExpansionHeight; } mQsMaxExpansionHeight = mQs.getDesiredHeight(); - mNotificationStackScroller.setMaxTopPadding( + mNotificationStackScrollLayoutController.setMaxTopPadding( mQsMaxExpansionHeight + mQsNotificationTopPadding); } positionClockAndNotifications(); @@ -3727,7 +3791,7 @@ public class NotificationPanelViewController extends PanelViewController { 0, calculateQsTopPadding(), mView.getWidth(), calculateQsTopPadding(), p); p.setColor(Color.CYAN); canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(), - mNotificationStackScroller.getTopPadding(), p); + mNotificationStackScrollLayoutController.getTopPadding(), p); p.setColor(Color.GRAY); canvas.drawLine(0, mClockPositionResult.clockY, mView.getWidth(), mClockPositionResult.clockY, p); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java index bc73be19ab59..3c43a1777171 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java @@ -43,11 +43,12 @@ import android.view.WindowManager.LayoutParams; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; -import com.android.systemui.statusbar.RemoteInputController.Callback; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -66,14 +67,13 @@ import java.util.Set; import java.util.function.Consumer; import javax.inject.Inject; -import javax.inject.Singleton; /** * Encapsulates all logic for the notification shade window state management. */ -@Singleton -public class NotificationShadeWindowController implements Callback, Dumpable, - ConfigurationListener { +@SysUISingleton +public class NotificationShadeWindowControllerImpl implements NotificationShadeWindowController, + Dumpable, ConfigurationListener { private static final String TAG = "NotificationShadeWindowController"; private static final boolean DEBUG = false; @@ -103,7 +103,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable, private final SysuiColorExtractor mColorExtractor; @Inject - public NotificationShadeWindowController(Context context, WindowManager windowManager, + public NotificationShadeWindowControllerImpl(Context context, WindowManager windowManager, IActivityManager activityManager, DozeParameters dozeParameters, StatusBarStateController statusBarStateController, ConfigurationController configurationController, @@ -147,6 +147,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable, /** * Register to receive notifications about status bar window state changes. */ + @Override public void registerCallback(StatusBarWindowCallback callback) { // Prevent adding duplicate callbacks for (int i = 0; i < mCallbacks.size(); i++) { @@ -161,6 +162,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable, * Register a listener to monitor scrims visibility * @param listener A listener to monitor scrims visibility */ + @Override public void setScrimsVisibilityListener(Consumer<Integer> listener) { if (listener != null && mScrimsVisibilityListener != listener) { mScrimsVisibilityListener = listener; @@ -176,6 +178,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable, /** * Adds the notification shade view to the window manager. */ + @Override public void attach() { // Now that the notification shade encompasses the sliding panel and its // translucent backdrop, the entire thing is made TRANSLUCENT and is @@ -214,14 +217,17 @@ public class NotificationShadeWindowController implements Callback, Dumpable, } } + @Override public void setNotificationShadeView(ViewGroup view) { mNotificationShadeView = view; } + @Override public ViewGroup getNotificationShadeView() { return mNotificationShadeView; } + @Override public void setDozeScreenBrightness(int value) { mScreenBrightnessDoze = value / 255f; } @@ -406,6 +412,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable, notifyStateChangedCallbacks(); } + @Override public void notifyStateChangedCallbacks() { for (int i = 0; i < mCallbacks.size(); i++) { StatusBarWindowCallback cb = mCallbacks.get(i).get(); @@ -445,62 +452,74 @@ public class NotificationShadeWindowController implements Callback, Dumpable, } } + @Override public void setKeyguardShowing(boolean showing) { mCurrentState.mKeyguardShowing = showing; apply(mCurrentState); } + @Override public void setKeyguardOccluded(boolean occluded) { mCurrentState.mKeyguardOccluded = occluded; apply(mCurrentState); } + @Override public void setKeyguardNeedsInput(boolean needsInput) { mCurrentState.mKeyguardNeedsInput = needsInput; apply(mCurrentState); } + @Override public void setPanelVisible(boolean visible) { mCurrentState.mPanelVisible = visible; mCurrentState.mNotificationShadeFocusable = visible; apply(mCurrentState); } + @Override public void setNotificationShadeFocusable(boolean focusable) { mCurrentState.mNotificationShadeFocusable = focusable; apply(mCurrentState); } + @Override public void setBouncerShowing(boolean showing) { mCurrentState.mBouncerShowing = showing; apply(mCurrentState); } + @Override public void setBackdropShowing(boolean showing) { mCurrentState.mBackdropShowing = showing; apply(mCurrentState); } + @Override public void setKeyguardFadingAway(boolean keyguardFadingAway) { mCurrentState.mKeyguardFadingAway = keyguardFadingAway; apply(mCurrentState); } + @Override public void setQsExpanded(boolean expanded) { mCurrentState.mQsExpanded = expanded; apply(mCurrentState); } + @Override public void setForceUserActivity(boolean forceUserActivity) { mCurrentState.mForceUserActivity = forceUserActivity; apply(mCurrentState); } - void setLaunchingActivity(boolean launching) { + @Override + public void setLaunchingActivity(boolean launching) { mCurrentState.mLaunchingActivity = launching; apply(mCurrentState); } + @Override public void setScrimsVisibility(int scrimsVisibility) { mCurrentState.mScrimsVisibility = scrimsVisibility; apply(mCurrentState); @@ -512,6 +531,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable, * {@link com.android.systemui.statusbar.NotificationShadeDepthController}. * @param backgroundBlurRadius Radius in pixels. */ + @Override public void setBackgroundBlurRadius(int backgroundBlurRadius) { if (mCurrentState.mBackgroundBlurRadius == backgroundBlurRadius) { return; @@ -520,11 +540,13 @@ public class NotificationShadeWindowController implements Callback, Dumpable, apply(mCurrentState); } + @Override public void setHeadsUpShowing(boolean showing) { mCurrentState.mHeadsUpShowing = showing; apply(mCurrentState); } + @Override public void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) { mCurrentState.mWallpaperSupportsAmbientMode = supportsAmbientMode; apply(mCurrentState); @@ -543,11 +565,13 @@ public class NotificationShadeWindowController implements Callback, Dumpable, * Used for when a heads-up comes in but we still need to wait for the touchable regions to * be computed. */ + @Override public void setForceWindowCollapsed(boolean force) { mCurrentState.mForceCollapsed = force; apply(mCurrentState); } + @Override public void setPanelExpanded(boolean isExpanded) { mCurrentState.mPanelExpanded = isExpanded; apply(mCurrentState); @@ -563,16 +587,19 @@ public class NotificationShadeWindowController implements Callback, Dumpable, * Set whether the screen brightness is forced to the value we use for doze mode by the status * bar window. */ + @Override public void setForceDozeBrightness(boolean forceDozeBrightness) { mCurrentState.mForceDozeBrightness = forceDozeBrightness; apply(mCurrentState); } + @Override public void setDozing(boolean dozing) { mCurrentState.mDozing = dozing; apply(mCurrentState); } + @Override public void setForcePluginOpen(boolean forcePluginOpen) { mCurrentState.mForcePluginOpen = forcePluginOpen; apply(mCurrentState); @@ -584,10 +611,12 @@ public class NotificationShadeWindowController implements Callback, Dumpable, /** * The forcePluginOpen state for the status bar. */ + @Override public boolean getForcePluginOpen() { return mCurrentState.mForcePluginOpen; } + @Override public void setNotTouchable(boolean notTouchable) { mCurrentState.mNotTouchable = notTouchable; apply(mCurrentState); @@ -596,24 +625,29 @@ public class NotificationShadeWindowController implements Callback, Dumpable, /** * Whether the status bar panel is expanded or not. */ + @Override public boolean getPanelExpanded() { return mCurrentState.mPanelExpanded; } + @Override public void setStateListener(OtherwisedCollapsedListener listener) { mListener = listener; } + @Override public void setForcePluginOpenListener(ForcePluginOpenListener listener) { mForcePluginOpenListener = listener; } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(TAG + ":"); pw.println(" mKeyguardDisplayMode=" + mKeyguardDisplayMode); pw.println(mCurrentState); } + @Override public boolean isShowingWallpaper() { return !mCurrentState.mBackdropShowing; } @@ -632,6 +666,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable, /** * When keyguard will be dismissed but didn't start animation yet. */ + @Override public void setKeyguardGoingAway(boolean goingAway) { mCurrentState.mKeyguardGoingAway = goingAway; apply(mCurrentState); @@ -642,6 +677,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable, * animation is performed, the component should remove itself from the list of features that * are forcing SystemUI to be top-ui. */ + @Override public void setRequestTopUi(boolean requestTopUi, String componentTag) { if (requestTopUi) { mCurrentState.mComponentsForcingTopUi.add(componentTag); @@ -725,23 +761,4 @@ public class NotificationShadeWindowController implements Callback, Dumpable, setDozing(isDozing); } }; - - /** - * Custom listener to pipe data back to plugins about whether or not the status bar would be - * collapsed if not for the plugin. - * TODO: Find cleaner way to do this. - */ - public interface OtherwisedCollapsedListener { - void setWouldOtherwiseCollapse(boolean otherwiseCollapse); - } - - /** - * Listener to indicate forcePluginOpen has changed - */ - public interface ForcePluginOpenListener { - /** - * Called when mState.forcePluginOpen is changed - */ - void onChange(boolean forceOpen); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java index 42222d724896..53cc2676723c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java @@ -44,6 +44,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShadeDepthController; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 8cb54eef01b4..e42c3dc4f589 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -284,13 +284,22 @@ public class PhoneStatusBarPolicy mResources.getString(R.string.accessibility_data_saver_on)); mIconController.setIconVisibility(mSlotDataSaver, false); + // privacy items + String microphoneString = mResources.getString(PrivacyType.TYPE_MICROPHONE.getNameId()); + String microphoneDesc = mResources.getString( + R.string.ongoing_privacy_chip_content_multiple_apps, microphoneString); mIconController.setIcon(mSlotMicrophone, PrivacyType.TYPE_MICROPHONE.getIconId(), - mResources.getString(PrivacyType.TYPE_MICROPHONE.getNameId())); + microphoneDesc); mIconController.setIconVisibility(mSlotMicrophone, false); + + String cameraString = mResources.getString(PrivacyType.TYPE_CAMERA.getNameId()); + String cameraDesc = mResources.getString( + R.string.ongoing_privacy_chip_content_multiple_apps, cameraString); mIconController.setIcon(mSlotCamera, PrivacyType.TYPE_CAMERA.getIconId(), - mResources.getString(PrivacyType.TYPE_CAMERA.getNameId())); + cameraDesc); mIconController.setIconVisibility(mSlotCamera, false); + mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID, mResources.getString(R.string.accessibility_location_active)); mIconController.setIconVisibility(mSlotLocation, false); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 686b87127239..11d05830d065 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -46,6 +46,7 @@ import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.statusbar.ScrimView; @@ -62,13 +63,12 @@ import java.lang.annotation.RetentionPolicy; import java.util.function.Consumer; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controls both the scrim behind the notifications and in front of the notifications (when a * security method gets shown). */ -@Singleton +@SysUISingleton public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnColorsChangedListener, Dumpable { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java index 333061547d7e..1ce22194878f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java @@ -23,20 +23,21 @@ import android.view.WindowManager; import com.android.systemui.assist.AssistManager; import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationPresenter; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; /** An implementation of {@link com.android.systemui.statusbar.phone.ShadeController}. */ -@Singleton +@SysUISingleton public class ShadeControllerImpl implements ShadeController { private static final String TAG = "ShadeControllerImpl"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index c8fb4e3372c8..6e37f90f9d94 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -138,14 +138,12 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.ActivityIntentHelper; import com.android.systemui.AutoReinflateContainer; import com.android.systemui.DejankUtils; -import com.android.systemui.DemoMode; import com.android.systemui.Dumpable; import com.android.systemui.EventLogTags; import com.android.systemui.InitController; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.SystemUI; -import com.android.systemui.SystemUIFactory; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.bubbles.BubbleController; @@ -153,12 +151,17 @@ import com.android.systemui.charging.WirelessChargingAnimation; import com.android.systemui.classifier.FalsingLog; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.qualifiers.UiBackground; +import com.android.systemui.demomode.DemoMode; +import com.android.systemui.demomode.DemoModeCommandReceiver; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.fragments.ExtensionFragmentListener; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.navigationbar.NavigationBarController; +import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.FalsingManager; @@ -182,12 +185,12 @@ import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.KeyboardShortcuts; import com.android.systemui.statusbar.KeyguardIndicationController; -import com.android.systemui.statusbar.NavigationBarController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.PulseExpansionHandler; @@ -200,8 +203,8 @@ import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; @@ -209,6 +212,8 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule; import com.android.systemui.statusbar.policy.BatteryController; @@ -230,6 +235,8 @@ import com.android.systemui.volume.VolumeComponent; import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.Executor; @@ -388,12 +395,13 @@ public class StatusBar extends SystemUI implements DemoMode, private final SuperStatusBarViewFactory mSuperStatusBarViewFactory; private final LightsOutNotifController mLightsOutNotifController; private final InitController mInitController; - private final DarkIconDispatcher mDarkIconDispatcher; + private final PluginDependencyProvider mPluginDependencyProvider; private final KeyguardDismissUtil mKeyguardDismissUtil; private final ExtensionController mExtensionController; private final UserInfoControllerImpl mUserInfoControllerImpl; private final DismissCallbackRegistry mDismissCallbackRegistry; + private final DemoModeController mDemoModeController; private NotificationsController mNotificationsController; // expanded notifications @@ -600,7 +608,7 @@ public class StatusBar extends SystemUI implements DemoMode, private UiModeManager mUiModeManager; protected boolean mIsKeyguard; private LogMaker mStatusBarStateLog; - protected NotificationIconAreaController mNotificationIconAreaController; + protected final NotificationIconAreaController mNotificationIconAreaController; @Nullable private View mAmbientIndicationContainer; private final SysuiColorExtractor mColorExtractor; private final ScreenLifecycle mScreenLifecycle; @@ -645,6 +653,7 @@ public class StatusBar extends SystemUI implements DemoMode, private final BubbleController.BubbleExpandListener mBubbleExpandListener; private ActivityIntentHelper mActivityIntentHelper; + private NotificationStackScrollLayoutController mStackScrollerController; /** * Public constructor for StatusBar. @@ -721,7 +730,6 @@ public class StatusBar extends SystemUI implements DemoMode, StatusBarKeyguardViewManager statusBarKeyguardViewManager, ViewMediatorCallback viewMediatorCallback, InitController initController, - DarkIconDispatcher darkIconDispatcher, @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler, PluginDependencyProvider pluginDependencyProvider, KeyguardDismissUtil keyguardDismissUtil, @@ -730,8 +738,10 @@ public class StatusBar extends SystemUI implements DemoMode, PhoneStatusBarPolicy phoneStatusBarPolicy, KeyguardIndicationController keyguardIndicationController, DismissCallbackRegistry dismissCallbackRegistry, + DemoModeController demoModeController, Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy, - StatusBarTouchableRegionManager statusBarTouchableRegionManager) { + StatusBarTouchableRegionManager statusBarTouchableRegionManager, + NotificationIconAreaController notificationIconAreaController) { super(context); mNotificationsController = notificationsController; mLightBarController = lightBarController; @@ -801,13 +811,14 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mKeyguardViewMediatorCallback = viewMediatorCallback; mInitController = initController; - mDarkIconDispatcher = darkIconDispatcher; mPluginDependencyProvider = pluginDependencyProvider; mKeyguardDismissUtil = keyguardDismissUtil; mExtensionController = extensionController; mUserInfoControllerImpl = userInfoControllerImpl; mIconPolicy = phoneStatusBarPolicy; mDismissCallbackRegistry = dismissCallbackRegistry; + mDemoModeController = demoModeController; + mNotificationIconAreaController = notificationIconAreaController; mBubbleExpandListener = (isExpanding, key) -> { @@ -862,6 +873,9 @@ public class StatusBar extends SystemUI implements DemoMode, // Connect in to the status bar manager service mCommandQueue.addCallback(this); + // Listen for demo mode changes + mDemoModeController.addCallback(this); + RegisterStatusBarResult result = null; try { result = mBarService.registerStatusBar(mCommandQueue); @@ -938,9 +952,12 @@ public class StatusBar extends SystemUI implements DemoMode, startKeyguard(); mKeyguardUpdateMonitor.registerCallback(mUpdateCallback); - mDozeServiceHost.initialize(this, mNotificationIconAreaController, - mStatusBarKeyguardViewManager, mNotificationShadeWindowViewController, - mNotificationPanelViewController, mAmbientIndicationContainer); + mDozeServiceHost.initialize( + this, + mStatusBarKeyguardViewManager, + mNotificationShadeWindowViewController, + mNotificationPanelViewController, + mAmbientIndicationContainer); mConfigurationController.addCallback(this); @@ -1015,25 +1032,19 @@ public class StatusBar extends SystemUI implements DemoMode, // TODO: Deal with the ugliness that comes from having some of the statusbar broken out // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot. - mStackScroller = mNotificationShadeWindowView.findViewById( - R.id.notification_stack_scroller); - NotificationListContainer notifListContainer = (NotificationListContainer) mStackScroller; + mStackScrollerController = + mNotificationPanelViewController.getNotificationStackScrollLayoutController(); + mStackScroller = mStackScrollerController.getView(); + NotificationListContainer notifListContainer = + mStackScrollerController.getNotificationListContainer(); mNotificationLogger.setUpWithContainer(notifListContainer); - // TODO: make this injectable. Currently that would create a circular dependency between - // NotificationIconAreaController and StatusBar. - mNotificationIconAreaController = SystemUIFactory.getInstance() - .createNotificationIconAreaController(context, this, - mWakeUpCoordinator, mKeyguardBypassController, - mStatusBarStateController); - mWakeUpCoordinator.setIconAreaController(mNotificationIconAreaController); + updateAodIconArea(); inflateShelf(); mNotificationIconAreaController.setupShelf(mNotificationShelfController); - mNotificationPanelViewController.setOnReinflationListener( - mNotificationIconAreaController::initAodIcons); + mNotificationPanelViewController.setOnReinflationListener(this::updateAodIconArea); mNotificationPanelViewController.addExpansionListener(mWakeUpCoordinator); - mDarkIconDispatcher.addDarkReceiver(mNotificationIconAreaController); // Allow plugins to reference DarkIconDispatcher and StatusBarStateController mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class); mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class); @@ -1075,7 +1086,7 @@ public class StatusBar extends SystemUI implements DemoMode, // TODO (b/136993073) Separate notification shade and status bar mHeadsUpAppearanceController = new HeadsUpAppearanceController( mNotificationIconAreaController, mHeadsUpManager, - mNotificationShadeWindowView, + mStackScroller.getController(), mStatusBarStateController, mKeyguardBypassController, mKeyguardStateController, mWakeUpCoordinator, mCommandQueue, mNotificationPanelViewController, mStatusBarView); @@ -1146,9 +1157,11 @@ public class StatusBar extends SystemUI implements DemoMode, }); mScrimController.attachViews(scrimBehind, scrimInFront, scrimForBubble); - mNotificationPanelViewController.initDependencies(this, mGroupManager, + mNotificationPanelViewController.initDependencies( + this, + mGroupManager, mNotificationShelfController, - mNotificationIconAreaController, mScrimController); + mScrimController); BackDropView backdrop = mNotificationShadeWindowView.findViewById(R.id.backdrop); mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front), @@ -1248,7 +1261,6 @@ public class StatusBar extends SystemUI implements DemoMode, if (DEBUG_MEDIA_FAKE_ARTWORK) { demoFilter.addAction(ACTION_FAKE_ARTWORK); } - demoFilter.addAction(ACTION_DEMO); context.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter, android.Manifest.permission.DUMP, null); @@ -1263,6 +1275,12 @@ public class StatusBar extends SystemUI implements DemoMode, ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f)); } + private void updateAodIconArea() { + mNotificationIconAreaController.setupAodIcons( + getNotificationShadeWindowView() + .findViewById(R.id.clock_notification_icon_container)); + } + @NonNull @Override public Lifecycle getLifecycle() { @@ -1300,13 +1318,15 @@ public class StatusBar extends SystemUI implements DemoMode, mActivityLaunchAnimator = new ActivityLaunchAnimator( mNotificationShadeWindowViewController, this, mNotificationPanelViewController, mNotificationShadeDepthControllerLazy.get(), - (NotificationListContainer) mStackScroller, mContext.getMainExecutor()); + mStackScrollerController.getNotificationListContainer(), + mContext.getMainExecutor()); // TODO: inject this. mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController, - mHeadsUpManager, mNotificationShadeWindowView, mStackScroller, mDozeScrimController, - mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController, - mKeyguardStateController, mKeyguardIndicationController, + mHeadsUpManager, mNotificationShadeWindowView, mStackScrollerController, + mDozeScrimController, mScrimController, mActivityLaunchAnimator, + mDynamicPrivacyController, mKeyguardStateController, + mKeyguardIndicationController, this /* statusBar */, mShadeController, mCommandQueue, mInitController, mNotificationInterruptStateProvider); @@ -1320,16 +1340,13 @@ public class StatusBar extends SystemUI implements DemoMode, .setNotificationPresenter(mPresenter) .setNotificationPanelViewController(mNotificationPanelViewController) .build(); - - ((NotificationListContainer) mStackScroller) - .setNotificationActivityStarter(mNotificationActivityStarter); - + mStackScroller.setNotificationActivityStarter(mNotificationActivityStarter); mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); mNotificationsController.initialize( this, mPresenter, - (NotificationListContainer) mStackScroller, + mStackScrollerController.getNotificationListContainer(), mNotificationActivityStarter, mPresenter); } @@ -1459,6 +1476,34 @@ public class StatusBar extends SystemUI implements DemoMode, protected void startKeyguard() { Trace.beginSection("StatusBar#startKeyguard"); mBiometricUnlockController = mBiometricUnlockControllerLazy.get(); + mBiometricUnlockController.setBiometricModeListener( + new BiometricUnlockController.BiometricModeListener() { + @Override + public void onResetMode() { + setWakeAndUnlocking(false); + } + + @Override + public void onModeChanged(int mode) { + switch (mode) { + case BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM: + case BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING: + case BiometricUnlockController.MODE_WAKE_AND_UNLOCK: + setWakeAndUnlocking(true); + } + } + + @Override + public void notifyBiometricAuthModeChanged() { + StatusBar.this.notifyBiometricAuthModeChanged(); + } + + private void setWakeAndUnlocking(boolean wakeAndUnlocking) { + if (getNavigationBarView() != null) { + getNavigationBarView().setWakeAndUnlocking(wakeAndUnlocking); + } + } + }); mStatusBarKeyguardViewManager.registerStatusBar( /* statusBar= */ this, getBouncerContainer(), mNotificationPanelViewController, mBiometricUnlockController, @@ -1501,7 +1546,7 @@ public class StatusBar extends SystemUI implements DemoMode, return mStatusBarWindowController.getStatusBarHeight(); } - protected boolean toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) { + public boolean toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) { if (!mRecentsOptional.isPresent()) { return false; } @@ -1804,7 +1849,7 @@ public class StatusBar extends SystemUI implements DemoMode, mPanelExpanded = isExpanded; updateHideIconsForBouncer(false /* animate */); mNotificationShadeWindowController.setPanelExpanded(isExpanded); - mVisualStabilityManager.setPanelExpanded(isExpanded); + mStatusBarStateController.setPanelExpanded(isExpanded); if (isExpanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) { if (DEBUG) { Log.v(TAG, "clearing notification effects from setExpandedHeight"); @@ -2025,7 +2070,7 @@ public class StatusBar extends SystemUI implements DemoMode, mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); } mNotificationPanelViewController.expand(true /* animate */); - ((NotificationListContainer) mStackScroller).setWillExpand(true); + mStackScroller.setWillExpand(true); mHeadsUpManager.unpinAll(true /* userUnpinned */); mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1); } else if (!mNotificationPanelViewController.isInSettings() @@ -2434,8 +2479,8 @@ public class StatusBar extends SystemUI implements DemoMode, return mNotificationShadeWindowViewController.getBarTransitions(); } - void checkBarModes() { - if (mDemoMode) return; + public void checkBarModes() { + if (mDemoModeController.isInDemoMode()) return; if (mNotificationShadeWindowViewController != null && getStatusBarTransitions() != null) { checkBarMode(mStatusBarMode, mStatusBarWindowState, getStatusBarTransitions()); } @@ -2444,7 +2489,7 @@ public class StatusBar extends SystemUI implements DemoMode, } // Called by NavigationBarFragment - void setQsScrimEnabled(boolean scrimEnabled) { + public void setQsScrimEnabled(boolean scrimEnabled) { mNotificationPanelViewController.setQsScrimEnabled(scrimEnabled); } @@ -2617,7 +2662,7 @@ public class StatusBar extends SystemUI implements DemoMode, } } - static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) { + public static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) { pw.print(" "); pw.print(var); pw.print(".BarTransitions.mMode="); pw.println(BarTransitions.modeToString(transitions.getMode())); } @@ -2808,19 +2853,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void onReceive(Context context, Intent intent) { if (DEBUG) Log.v(TAG, "onReceive: " + intent); String action = intent.getAction(); - if (ACTION_DEMO.equals(action)) { - Bundle bundle = intent.getExtras(); - if (bundle != null) { - String command = bundle.getString("command", "").trim().toLowerCase(); - if (command.length() > 0) { - try { - dispatchDemoCommand(command, bundle); - } catch (Throwable t) { - Log.w(TAG, "Error running demo command, intent=" + intent, t); - } - } - } - } else if (ACTION_FAKE_ARTWORK.equals(action)) { + if (ACTION_FAKE_ARTWORK.equals(action)) { if (DEBUG_MEDIA_FAKE_ARTWORK) { mPresenter.updateMediaMetaData(true, true); } @@ -3084,50 +3117,34 @@ public class StatusBar extends SystemUI implements DemoMode, startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */); } - private boolean mDemoModeAllowed; - private boolean mDemoMode; + @Override + public List<String> demoCommands() { + List<String> s = new ArrayList<>(); + s.add(DemoMode.COMMAND_BARS); + s.add(DemoMode.COMMAND_CLOCK); + s.add(DemoMode.COMMAND_OPERATOR); + return s; + } @Override - public void dispatchDemoCommand(String command, Bundle args) { - if (!mDemoModeAllowed) { - mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(), - DEMO_MODE_ALLOWED, 0) != 0; - } - if (!mDemoModeAllowed) return; - if (command.equals(COMMAND_ENTER)) { - mDemoMode = true; - } else if (command.equals(COMMAND_EXIT)) { - mDemoMode = false; - checkBarModes(); - } else if (!mDemoMode) { - // automatically enter demo mode on first demo command - dispatchDemoCommand(COMMAND_ENTER, new Bundle()); - } - boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT); - if ((modeChange || command.equals(COMMAND_VOLUME)) && mVolumeComponent != null) { - mVolumeComponent.dispatchDemoCommand(command, args); - } - if (modeChange || command.equals(COMMAND_CLOCK)) { + public void onDemoModeStarted() { + // Must send this message to any view that we delegate to via dispatchDemoCommandToView + dispatchDemoModeStartedToView(R.id.clock); + dispatchDemoModeStartedToView(R.id.operator_name); + } + + @Override + public void onDemoModeFinished() { + dispatchDemoModeFinishedToView(R.id.clock); + dispatchDemoModeFinishedToView(R.id.operator_name); + checkBarModes(); + } + + @Override + public void dispatchDemoCommand(String command, @NonNull Bundle args) { + if (command.equals(COMMAND_CLOCK)) { dispatchDemoCommandToView(command, args, R.id.clock); } - if (modeChange || command.equals(COMMAND_BATTERY)) { - mBatteryController.dispatchDemoCommand(command, args); - } - if (modeChange || command.equals(COMMAND_STATUS)) { - ((StatusBarIconControllerImpl) mIconController).dispatchDemoCommand(command, args); - } - if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) { - mNetworkController.dispatchDemoCommand(command, args); - } - if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) { - View notifications = mStatusBarView == null ? null - : mStatusBarView.findViewById(R.id.notification_icon_area); - if (notifications != null) { - String visible = args.getString("visible"); - int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE; - notifications.setVisibility(vis); - } - } if (command.equals(COMMAND_BARS)) { String mode = args.getString("mode"); int barMode = "opaque".equals(mode) ? MODE_OPAQUE : @@ -3146,16 +3163,33 @@ public class StatusBar extends SystemUI implements DemoMode, mNavigationBarController.transitionTo(mDisplayId, barMode, animate); } } - if (modeChange || command.equals(COMMAND_OPERATOR)) { + if (command.equals(COMMAND_OPERATOR)) { dispatchDemoCommandToView(command, args, R.id.operator_name); } } + //TODO: these should have controllers, and this method should be removed private void dispatchDemoCommandToView(String command, Bundle args, int id) { if (mStatusBarView == null) return; View v = mStatusBarView.findViewById(id); - if (v instanceof DemoMode) { - ((DemoMode)v).dispatchDemoCommand(command, args); + if (v instanceof DemoModeCommandReceiver) { + ((DemoModeCommandReceiver) v).dispatchDemoCommand(command, args); + } + } + + private void dispatchDemoModeStartedToView(int id) { + if (mStatusBarView == null) return; + View v = mStatusBarView.findViewById(id); + if (v instanceof DemoModeCommandReceiver) { + ((DemoModeCommandReceiver) v).onDemoModeStarted(); + } + } + + private void dispatchDemoModeFinishedToView(int id) { + if (mStatusBarView == null) return; + View v = mStatusBarView.findViewById(id); + if (v instanceof DemoModeCommandReceiver) { + ((DemoModeCommandReceiver) v).onDemoModeFinished(); } } @@ -3753,7 +3787,6 @@ public class StatusBar extends SystemUI implements DemoMode, mDeviceInteractive = false; mWakeUpComingFromTouch = false; mWakeUpTouchLocation = null; - mVisualStabilityManager.setScreenOn(false); updateVisibleToUser(); updateNotificationPanelTouchState(); @@ -3790,7 +3823,6 @@ public class StatusBar extends SystemUI implements DemoMode, if (!mKeyguardBypassController.getBypassEnabled()) { mHeadsUpManager.releaseAllImmediately(); } - mVisualStabilityManager.setScreenOn(true); updateVisibleToUser(); updateIsKeyguard(); mDozeServiceHost.stopDozing(); @@ -4050,7 +4082,7 @@ public class StatusBar extends SystemUI implements DemoMode, protected IStatusBarService mBarService; // all notifications - protected ViewGroup mStackScroller; + protected NotificationStackScrollLayout mStackScroller; private final NotificationGroupManager mGroupManager; @@ -4131,7 +4163,7 @@ public class StatusBar extends SystemUI implements DemoMode, toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */); } - void awakenDreams() { + public void awakenDreams() { mUiBgExecutor.execute(() -> { try { mDreamManager.awaken(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index 8ff7a41cb22f..f0efed332c7f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -32,9 +32,9 @@ import android.widget.LinearLayout.LayoutParams; import androidx.annotation.VisibleForTesting; import com.android.internal.statusbar.StatusBarIcon; -import com.android.systemui.DemoMode; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.demomode.DemoModeCommandReceiver; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.CommandQueue; @@ -198,7 +198,7 @@ public interface StatusBarIconController { /** * Turns info from StatusBarIconController into ImageViews in a ViewGroup. */ - public static class IconManager implements DemoMode { + class IconManager implements DemoModeCommandReceiver { protected final ViewGroup mGroup; protected final Context mContext; protected final int mIconSize; @@ -390,18 +390,24 @@ public interface StatusBarIconController { return; } - if (command.equals(COMMAND_EXIT)) { - if (mDemoStatusIcons != null) { - mDemoStatusIcons.dispatchDemoCommand(command, args); - exitDemoMode(); - } + mDemoStatusIcons.dispatchDemoCommand(command, args); + } + + @Override + public void onDemoModeStarted() { + mIsInDemoMode = true; + if (mDemoStatusIcons == null) { + mDemoStatusIcons = createDemoStatusIcons(); + } + mDemoStatusIcons.onDemoModeStarted(); + } + + @Override + public void onDemoModeFinished() { + if (mDemoStatusIcons != null) { + mDemoStatusIcons.onDemoModeFinished(); + exitDemoMode(); mIsInDemoMode = false; - } else { - if (mDemoStatusIcons == null) { - mIsInDemoMode = true; - mDemoStatusIcons = createDemoStatusIcons(); - } - mDemoStatusIcons.dispatchDemoCommand(command, args); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index 21e1d319cffa..2870152ed853 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -29,6 +29,9 @@ import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.demomode.DemoMode; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; @@ -45,31 +48,28 @@ import java.util.Collections; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * Receives the callbacks from CommandQueue related to icons and tracks the state of * all the icons. Dispatches this state to any IconManagers that are currently * registered with it. */ -@Singleton +@SysUISingleton public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable, - ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController { + ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController, DemoMode { private static final String TAG = "StatusBarIconController"; private final ArrayList<IconManager> mIconGroups = new ArrayList<>(); private final ArraySet<String> mIconHideList = new ArraySet<>(); - // Points to light or dark context depending on the... context? private Context mContext; - private Context mLightContext; - private Context mDarkContext; - - private boolean mIsDark = false; @Inject - public StatusBarIconControllerImpl(Context context, CommandQueue commandQueue) { + public StatusBarIconControllerImpl( + Context context, + CommandQueue commandQueue, + DemoModeController demoModeController) { super(context.getResources().getStringArray( com.android.internal.R.array.config_statusBarIcons)); Dependency.get(ConfigurationController.class).addCallback(this); @@ -80,6 +80,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu commandQueue.addCallback(this); Dependency.get(TunerService.class).addTunable(this, ICON_HIDE_LIST); + demoModeController.addCallback(this); } @Override @@ -339,6 +340,25 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu super.dump(pw); } + @Override + public void onDemoModeStarted() { + for (IconManager manager : mIconGroups) { + if (manager.isDemoable()) { + manager.onDemoModeStarted(); + } + } + } + + @Override + public void onDemoModeFinished() { + for (IconManager manager : mIconGroups) { + if (manager.isDemoable()) { + manager.onDemoModeFinished(); + } + } + } + + @Override public void dispatchDemoCommand(String command, Bundle args) { for (IconManager manager : mIconGroups) { if (manager.isDemoable()) { @@ -348,6 +368,13 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu } @Override + public List<String> demoCommands() { + List<String> s = new ArrayList<>(); + s.add(DemoMode.COMMAND_STATUS); + return s; + } + + @Override public void onDensityOrFontScaleChanged() { loadDimens(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 81d0699a29e6..7ee501c681f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -47,14 +47,17 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.settingslib.animation.AppearAnimationUtils; import com.android.systemui.DejankUtils; import com.android.systemui.SystemUIFactory; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.DismissCallbackRegistry; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; @@ -67,7 +70,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Singleton; /** * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back @@ -75,7 +77,7 @@ import javax.inject.Singleton; * which is in turn, reported to this class by the current * {@link com.android.keyguard.KeyguardViewBase}. */ -@Singleton +@SysUISingleton public class StatusBarKeyguardViewManager implements RemoteInputController.Callback, StatusBarStateController.StateListener, ConfigurationController.ConfigurationListener, PanelExpansionListener, NavigationModeController.ModeChangedListener, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 4de648402464..de11c9023200 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -49,7 +49,7 @@ import com.android.systemui.ActivityIntentHelper; import com.android.systemui.EventLogTags; import com.android.systemui.assist.AssistManager; import com.android.systemui.bubbles.BubbleController; -import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.plugins.ActivityStarter; @@ -73,14 +73,13 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.row.OnDismissCallback; +import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.policy.HeadsUpUtil; import com.android.systemui.statusbar.policy.KeyguardStateController; import java.util.concurrent.Executor; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; @@ -93,7 +92,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final CommandQueue mCommandQueue; private final Handler mMainThreadHandler; - private final Handler mBackgroundHandler; private final Executor mUiBgExecutor; private final NotificationEntryManager mEntryManager; @@ -126,7 +124,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final NotificationPresenter mPresenter; private final NotificationPanelViewController mNotificationPanel; private final ActivityLaunchAnimator mActivityLaunchAnimator; - private final OnDismissCallback mOnDismissCallback; + private final OnUserInteractionCallback mOnUserInteractionCallback; private boolean mIsCollapsingToShowActivityOverLockscreen; @@ -134,7 +132,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit Context context, CommandQueue commandQueue, Handler mainThreadHandler, - Handler backgroundHandler, Executor uiBgExecutor, NotificationEntryManager entryManager, NotifPipeline notifPipeline, @@ -161,7 +158,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit FeatureFlags featureFlags, MetricsLogger metricsLogger, StatusBarNotificationActivityStarterLogger logger, - OnDismissCallback onDismissCallback, + OnUserInteractionCallback onUserInteractionCallback, StatusBar statusBar, NotificationPresenter presenter, @@ -170,7 +167,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mContext = context; mCommandQueue = commandQueue; mMainThreadHandler = mainThreadHandler; - mBackgroundHandler = backgroundHandler; mUiBgExecutor = uiBgExecutor; mEntryManager = entryManager; mNotifPipeline = notifPipeline; @@ -197,7 +193,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mFeatureFlags = featureFlags; mMetricsLogger = metricsLogger; mLogger = logger; - mOnDismissCallback = onDismissCallback; + mOnUserInteractionCallback = onUserInteractionCallback; // TODO: use KeyguardStateController#isOccluded to remove this dependency mStatusBar = statusBar; @@ -307,7 +303,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); mShadeController.collapsePanel(); } else { - mBackgroundHandler.postAtFrontOfQueue(runnable); + runnable.run(); } return !mNotificationPanel.isFullyCollapsed(); } @@ -579,10 +575,10 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit // To avoid lags we're only performing the remove // after the shade was collapsed mShadeController.addPostCollapseAction( - () -> mOnDismissCallback.onDismiss(entry, REASON_CLICK) + () -> mOnUserInteractionCallback.onDismiss(entry, REASON_CLICK) ); } else { - mOnDismissCallback.onDismiss(entry, REASON_CLICK); + mOnUserInteractionCallback.onDismiss(entry, REASON_CLICK); } }); } @@ -600,12 +596,12 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit /** * Public builder for {@link StatusBarNotificationActivityStarter}. */ - @Singleton + @SysUISingleton public static class Builder { private final Context mContext; private final CommandQueue mCommandQueue; private final Handler mMainThreadHandler; - private final Handler mBackgroundHandler; + private final Executor mUiBgExecutor; private final NotificationEntryManager mEntryManager; private final NotifPipeline mNotifPipeline; @@ -632,7 +628,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final FeatureFlags mFeatureFlags; private final MetricsLogger mMetricsLogger; private final StatusBarNotificationActivityStarterLogger mLogger; - private final OnDismissCallback mOnDismissCallback; + private final OnUserInteractionCallback mOnUserInteractionCallback; private StatusBar mStatusBar; private NotificationPresenter mNotificationPresenter; @@ -644,7 +640,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit Context context, CommandQueue commandQueue, @Main Handler mainThreadHandler, - @Background Handler backgroundHandler, @UiBackground Executor uiBgExecutor, NotificationEntryManager entryManager, NotifPipeline notifPipeline, @@ -671,12 +666,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit FeatureFlags featureFlags, MetricsLogger metricsLogger, StatusBarNotificationActivityStarterLogger logger, - OnDismissCallback onDismissCallback) { + OnUserInteractionCallback onUserInteractionCallback) { mContext = context; mCommandQueue = commandQueue; mMainThreadHandler = mainThreadHandler; - mBackgroundHandler = backgroundHandler; mUiBgExecutor = uiBgExecutor; mEntryManager = entryManager; mNotifPipeline = notifPipeline; @@ -703,7 +697,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mFeatureFlags = featureFlags; mMetricsLogger = metricsLogger; mLogger = logger; - mOnDismissCallback = onDismissCallback; + mOnUserInteractionCallback = onUserInteractionCallback; } /** Sets the status bar to use as {@link StatusBar}. */ @@ -734,7 +728,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mContext, mCommandQueue, mMainThreadHandler, - mBackgroundHandler, mUiBgExecutor, mEntryManager, mNotifPipeline, @@ -761,7 +754,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mFeatureFlags, mMetricsLogger, mLogger, - mOnDismissCallback, + mOnUserInteractionCallback, mStatusBar, mNotificationPresenter, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 45f0c49a4fd4..67adaaae402e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -30,7 +30,6 @@ import android.service.vr.IVrStateCallbacks; import android.util.Log; import android.util.Slog; import android.view.View; -import android.view.ViewGroup; import android.view.accessibility.AccessibilityManager; import android.widget.TextView; @@ -53,6 +52,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; @@ -61,9 +61,9 @@ import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; @@ -71,7 +71,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener; -import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -133,7 +133,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, NotificationPanelViewController panel, HeadsUpManagerPhone headsUp, NotificationShadeWindowView statusBarWindow, - ViewGroup stackScroller, + NotificationStackScrollLayoutController stackScrollerController, DozeScrimController dozeScrimController, ScrimController scrimController, ActivityLaunchAnimator activityLaunchAnimator, @@ -155,7 +155,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, mStatusBar = statusBar; mShadeController = shadeController; mCommandQueue = commandQueue; - mAboveShelfObserver = new AboveShelfObserver(stackScroller); + mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView()); mActivityLaunchAnimator = activityLaunchAnimator; mAboveShelfObserver.setListener(statusBarWindow.findViewById( R.id.notification_container_parent)); @@ -190,7 +190,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, remoteInputManager.getController().addCallback( Dependency.get(NotificationShadeWindowController.class)); - NotificationListContainer notifListContainer = (NotificationListContainer) stackScroller; initController.addPostInitTask(() -> { NotificationEntryListener notificationEntryListener = new NotificationEntryListener() { @Override @@ -207,7 +206,8 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, } }; - mViewHierarchyManager.setUpWithPresenter(this, notifListContainer); + mViewHierarchyManager.setUpWithPresenter(this, + stackScrollerController.getNotificationListContainer()); mEntryManager.setUpWithPresenter(this); mEntryManager.addNotificationEntryListener(notificationEntryListener); mEntryManager.addNotificationLifetimeExtender(mHeadsUpManager); @@ -217,9 +217,9 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor); mLockscreenUserManager.setUpWithPresenter(this); mMediaManager.setUpWithPresenter(this); - mVisualStabilityManager.setUpWithPresenter(this); mGutsManager.setUpWithPresenter(this, - notifListContainer, mCheckSaveListener, mOnSettingsClickListener); + stackScrollerController.getNotificationListContainer(), mCheckSaveListener, + mOnSettingsClickListener); // ForegroundServiceNotificationListener adds its listener in its constructor // but we need to request it here in order for it to be instantiated. // TODO: figure out how to do this correctly once Dependency.get() is gone. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java index 72395e68ff07..8a8942975d2e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -34,6 +34,7 @@ import android.view.View; import android.view.ViewParent; import com.android.systemui.ActivityIntentHelper; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.ActionClickLogger; @@ -49,11 +50,10 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.policy.KeyguardStateController; import javax.inject.Inject; -import javax.inject.Singleton; /** */ -@Singleton +@SysUISingleton public class StatusBarRemoteInputCallback implements Callback, Callbacks, StatusBarStateController.StateListener { @@ -244,9 +244,10 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, @Override public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent, + boolean appRequestedAuth, NotificationRemoteInputManager.ClickHandler defaultHandler) { final boolean isActivity = pendingIntent.isActivity(); - if (isActivity) { + if (isActivity || appRequestedAuth) { mActionClickLogger.logWaitingToCloseKeyguard(pendingIntent); final boolean afterKeyguardGone = mActivityIntentHelper.wouldLaunchResolverActivity( pendingIntent.getIntent(), mLockscreenUserManager.getCurrentUserId()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java index 9c7f49090122..b859250a2442 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java @@ -32,6 +32,8 @@ import android.view.WindowInsets; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.ScreenDecorations; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; @@ -40,14 +42,13 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Singleton; /** * Manages what parts of the status bar are touchable. Clients are primarily UI that display in the * status bar even though the UI doesn't look like part of the status bar. Currently this consists * of HeadsUpNotifications. */ -@Singleton +@SysUISingleton public final class StatusBarTouchableRegionManager implements Dumpable { private static final String TAG = "TouchableRegionManager"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java index d4e1aa4d3d27..2f7278b38d15 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java @@ -29,16 +29,16 @@ import android.view.Gravity; import android.view.ViewGroup; import android.view.WindowManager; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import javax.inject.Inject; -import javax.inject.Singleton; /** * Encapsulates all logic for the status bar window state management. */ -@Singleton +@SysUISingleton public class StatusBarWindowController { private static final String TAG = "StatusBarWindowController"; private static final boolean DEBUG = false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneDependenciesModule.java index 69c6814090d1..79d72b3d0f65 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneDependenciesModule.java @@ -16,12 +16,11 @@ package com.android.systemui.statusbar.phone.dagger; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.row.RowContentBindStage; import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper; import com.android.systemui.statusbar.phone.StatusBar; -import javax.inject.Singleton; - import dagger.Module; import dagger.Provides; @@ -34,7 +33,7 @@ import dagger.Provides; public interface StatusBarPhoneDependenciesModule { /** */ - @Singleton + @SysUISingleton @Provides static NotificationGroupAlertTransferHelper provideNotificationGroupAlertTransferHelper( RowContentBindStage bindStage) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index 02e031217904..2768b826fcfe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -33,12 +33,14 @@ import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.UiBackground; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.plugins.DarkIconDispatcher; +import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.recents.Recents; @@ -47,11 +49,11 @@ import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; -import com.android.systemui.statusbar.NavigationBarController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.SuperStatusBarViewFactory; @@ -59,7 +61,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; @@ -79,7 +81,7 @@ import com.android.systemui.statusbar.phone.LightsOutNotifController; import com.android.systemui.statusbar.phone.LockscreenLockIconController; import com.android.systemui.statusbar.phone.LockscreenWallpaper; import com.android.systemui.statusbar.phone.NotificationGroupManager; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; +import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; @@ -104,7 +106,6 @@ import java.util.concurrent.Executor; import javax.inject.Named; import javax.inject.Provider; -import javax.inject.Singleton; import dagger.Lazy; import dagger.Module; @@ -119,7 +120,7 @@ public interface StatusBarPhoneModule { * Provides our instance of StatusBar which is considered optional. */ @Provides - @Singleton + @SysUISingleton static StatusBar provideStatusBar( Context context, NotificationsController notificationsController, @@ -188,7 +189,6 @@ public interface StatusBarPhoneModule { StatusBarKeyguardViewManager statusBarKeyguardViewManager, ViewMediatorCallback viewMediatorCallback, InitController initController, - DarkIconDispatcher darkIconDispatcher, @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler, PluginDependencyProvider pluginDependencyProvider, KeyguardDismissUtil keyguardDismissUtil, @@ -196,9 +196,11 @@ public interface StatusBarPhoneModule { UserInfoControllerImpl userInfoControllerImpl, PhoneStatusBarPolicy phoneStatusBarPolicy, KeyguardIndicationController keyguardIndicationController, + DemoModeController demoModeController, Lazy<NotificationShadeDepthController> notificationShadeDepthController, DismissCallbackRegistry dismissCallbackRegistry, - StatusBarTouchableRegionManager statusBarTouchableRegionManager) { + StatusBarTouchableRegionManager statusBarTouchableRegionManager, + NotificationIconAreaController notificationIconAreaController) { return new StatusBar( context, notificationsController, @@ -266,7 +268,6 @@ public interface StatusBarPhoneModule { statusBarKeyguardViewManager, viewMediatorCallback, initController, - darkIconDispatcher, timeTickHandler, pluginDependencyProvider, keyguardDismissUtil, @@ -275,7 +276,9 @@ public interface StatusBarPhoneModule { phoneStatusBarPolicy, keyguardIndicationController, dismissCallbackRegistry, + demoModeController, notificationShadeDepthController, - statusBarTouchableRegionManager); + statusBarTouchableRegionManager, + notificationIconAreaController); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java index ebfdb3f9aa76..ad49c796f91d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java @@ -19,16 +19,17 @@ package com.android.systemui.statusbar.policy; import android.content.Context; import android.view.accessibility.AccessibilityManager; +import com.android.systemui.dagger.SysUISingleton; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Singleton; /** */ -@Singleton +@SysUISingleton public class AccessibilityController implements AccessibilityManager.AccessibilityStateChangeListener, AccessibilityManager.TouchExplorationStateChangeListener { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java index 1395e1377529..d38284a26a07 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java @@ -19,13 +19,16 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; +import androidx.annotation.NonNull; + +import com.android.systemui.dagger.SysUISingleton; + import javax.inject.Inject; -import javax.inject.Singleton; /** * For mocking because AccessibilityManager is final for some reason... */ -@Singleton +@SysUISingleton public class AccessibilityManagerWrapper implements CallbackController<AccessibilityServicesStateChangeListener> { @@ -37,12 +40,12 @@ public class AccessibilityManagerWrapper implements } @Override - public void addCallback(AccessibilityServicesStateChangeListener listener) { + public void addCallback(@NonNull AccessibilityServicesStateChangeListener listener) { mAccessibilityManager.addAccessibilityServicesStateChangeListener(listener, null); } @Override - public void removeCallback(AccessibilityServicesStateChangeListener listener) { + public void removeCallback(@NonNull AccessibilityServicesStateChangeListener listener) { mAccessibilityManager.removeAccessibilityServicesStateChangeListener(listener); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java index e5a46797d035..06e4731265e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -18,8 +18,8 @@ package com.android.systemui.statusbar.policy; import android.annotation.Nullable; -import com.android.systemui.DemoMode; import com.android.systemui.Dumpable; +import com.android.systemui.demomode.DemoMode; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; import java.io.FileDescriptor; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index 88a6263c1dca..57ac85e1e86d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -27,6 +27,7 @@ import android.os.PowerManager; import android.os.PowerSaveState; import android.util.Log; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; @@ -34,22 +35,25 @@ import com.android.settingslib.fuelgauge.BatterySaverUtils; import com.android.settingslib.fuelgauge.Estimate; import com.android.settingslib.utils.PowerUtil; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.demomode.DemoMode; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.power.EnhancedEstimates; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * Default implementation of a {@link BatteryController}. This controller monitors for battery * level change events that are broadcasted by the system. */ -@Singleton +@SysUISingleton public class BatteryControllerImpl extends BroadcastReceiver implements BatteryController { private static final String TAG = "BatteryController"; @@ -63,6 +67,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC mChangeCallbacks = new ArrayList<>(); private final ArrayList<EstimateFetchCompletion> mFetchCallbacks = new ArrayList<>(); private final PowerManager mPowerManager; + private final DemoModeController mDemoModeController; private final Handler mMainHandler; private final Handler mBgHandler; protected final Context mContext; @@ -82,15 +87,21 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC @VisibleForTesting @Inject - public BatteryControllerImpl(Context context, EnhancedEstimates enhancedEstimates, - PowerManager powerManager, BroadcastDispatcher broadcastDispatcher, - @Main Handler mainHandler, @Background Handler bgHandler) { + public BatteryControllerImpl( + Context context, + EnhancedEstimates enhancedEstimates, + PowerManager powerManager, + BroadcastDispatcher broadcastDispatcher, + DemoModeController demoModeController, + @Main Handler mainHandler, + @Background Handler bgHandler) { mContext = context; mMainHandler = mainHandler; mBgHandler = bgHandler; mPowerManager = powerManager; mEstimates = enhancedEstimates; mBroadcastDispatcher = broadcastDispatcher; + mDemoModeController = demoModeController; } private void registerReceiver() { @@ -114,6 +125,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC onReceive(mContext, intent); } } + mDemoModeController.addCallback(this); updatePowerSave(); updateEstimate(); } @@ -134,7 +146,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC } @Override - public void addCallback(BatteryController.BatteryStateChangeCallback cb) { + public void addCallback(@NonNull BatteryController.BatteryStateChangeCallback cb) { synchronized (mChangeCallbacks) { mChangeCallbacks.add(cb); } @@ -144,7 +156,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC } @Override - public void removeCallback(BatteryController.BatteryStateChangeCallback cb) { + public void removeCallback(@NonNull BatteryController.BatteryStateChangeCallback cb) { synchronized (mChangeCallbacks) { mChangeCallbacks.remove(cb); } @@ -325,32 +337,43 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC } } - private boolean mDemoMode; - @Override public void dispatchDemoCommand(String command, Bundle args) { - if (!mDemoMode && command.equals(COMMAND_ENTER)) { - mDemoMode = true; - mBroadcastDispatcher.unregisterReceiver(this); - } else if (mDemoMode && command.equals(COMMAND_EXIT)) { - mDemoMode = false; - registerReceiver(); - updatePowerSave(); - } else if (mDemoMode && command.equals(COMMAND_BATTERY)) { - String level = args.getString("level"); - String plugged = args.getString("plugged"); - String powerSave = args.getString("powersave"); - if (level != null) { - mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100); - } - if (plugged != null) { - mPluggedIn = Boolean.parseBoolean(plugged); - } - if (powerSave != null) { - mPowerSave = powerSave.equals("true"); - firePowerSaveChanged(); - } - fireBatteryLevelChanged(); + if (!mDemoModeController.isInDemoMode()) { + return; + } + + String level = args.getString("level"); + String plugged = args.getString("plugged"); + String powerSave = args.getString("powersave"); + if (level != null) { + mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100); + } + if (plugged != null) { + mPluggedIn = Boolean.parseBoolean(plugged); + } + if (powerSave != null) { + mPowerSave = powerSave.equals("true"); + firePowerSaveChanged(); } + fireBatteryLevelChanged(); + } + + @Override + public List<String> demoCommands() { + List<String> s = new ArrayList<>(); + s.add(DemoMode.COMMAND_BATTERY); + return s; + } + + @Override + public void onDemoModeStarted() { + mBroadcastDispatcher.unregisterReceiver(this); + } + + @Override + public void onDemoModeFinished() { + registerReceiver(); + updatePowerSave(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java index 0fc3d8481907..33b1a4a880ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java @@ -29,11 +29,14 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.Log; +import androidx.annotation.NonNull; + import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfile; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -46,11 +49,10 @@ import java.util.List; import java.util.WeakHashMap; import javax.inject.Inject; -import javax.inject.Singleton; /** */ -@Singleton +@SysUISingleton public class BluetoothControllerImpl implements BluetoothController, BluetoothCallback, CachedBluetoothDevice.Callback, LocalBluetoothProfileManager.ServiceListener { private static final String TAG = "BluetoothController"; @@ -150,13 +152,13 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa } @Override - public void addCallback(Callback cb) { + public void addCallback(@NonNull Callback cb) { mHandler.obtainMessage(H.MSG_ADD_CALLBACK, cb).sendToTarget(); mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED); } @Override - public void removeCallback(Callback cb) { + public void removeCallback(@NonNull Callback cb) { mHandler.obtainMessage(H.MSG_REMOVE_CALLBACK, cb).sendToTarget(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java index 78111fb61fd0..a0b03e1c54c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java @@ -123,13 +123,13 @@ public class BrightnessMirrorController } @Override - public void addCallback(BrightnessMirrorListener listener) { + public void addCallback(@NonNull BrightnessMirrorListener listener) { Objects.requireNonNull(listener); mBrightnessMirrorListeners.add(listener); } @Override - public void removeCallback(BrightnessMirrorListener listener) { + public void removeCallback(@NonNull BrightnessMirrorListener listener) { mBrightnessMirrorListeners.remove(listener); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java index 626eef5867f2..047ff75468ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java @@ -15,14 +15,19 @@ package com.android.systemui.statusbar.policy; +import androidx.annotation.NonNull; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle.Event; import androidx.lifecycle.LifecycleEventObserver; import androidx.lifecycle.LifecycleOwner; public interface CallbackController<T> { - void addCallback(T listener); - void removeCallback(T listener); + + /** Add a callback */ + void addCallback(@NonNull T listener); + + /** Remove a callback */ + void removeCallback(@NonNull T listener); /** * Wrapper to {@link #addCallback(Object)} when a lifecycle is in the resumed state diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java index 6106f38c0e60..7bde31592965 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java @@ -31,10 +31,12 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.internal.annotations.GuardedBy; import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.util.Utils; @@ -46,11 +48,10 @@ import java.util.Objects; import java.util.UUID; import javax.inject.Inject; -import javax.inject.Singleton; /** Platform implementation of the cast controller. **/ -@Singleton +@SysUISingleton public class CastControllerImpl implements CastController { private static final String TAG = "CastController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -95,7 +96,7 @@ public class CastControllerImpl implements CastController { } @Override - public void addCallback(Callback callback) { + public void addCallback(@NonNull Callback callback) { synchronized (mCallbacks) { mCallbacks.add(callback); } @@ -106,7 +107,7 @@ public class CastControllerImpl implements CastController { } @Override - public void removeCallback(Callback callback) { + public void removeCallback(@NonNull Callback callback) { synchronized (mCallbacks) { mCallbacks.remove(callback); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index 120a0e3abba4..ef35a3c55ab8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -40,11 +40,11 @@ import android.view.View; import android.widget.TextView; import com.android.settingslib.Utils; -import com.android.systemui.DemoMode; import com.android.systemui.Dependency; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.demomode.DemoModeCommandReceiver; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.settings.CurrentUserTracker; @@ -62,7 +62,10 @@ import java.util.TimeZone; /** * Digital clock for the status bar. */ -public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.Callbacks, +public class Clock extends TextView implements + DemoModeCommandReceiver, + Tunable, + CommandQueue.Callbacks, DarkReceiver, ConfigurationListener { public static final String CLOCK_SECONDS = "clock_seconds"; @@ -467,30 +470,35 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C @Override public void dispatchDemoCommand(String command, Bundle args) { - if (!mDemoMode && command.equals(COMMAND_ENTER)) { - mDemoMode = true; - } else if (mDemoMode && command.equals(COMMAND_EXIT)) { - mDemoMode = false; - updateClock(); - } else if (mDemoMode && command.equals(COMMAND_CLOCK)) { - String millis = args.getString("millis"); - String hhmm = args.getString("hhmm"); - if (millis != null) { - mCalendar.setTimeInMillis(Long.parseLong(millis)); - } else if (hhmm != null && hhmm.length() == 4) { - int hh = Integer.parseInt(hhmm.substring(0, 2)); - int mm = Integer.parseInt(hhmm.substring(2)); - boolean is24 = DateFormat.is24HourFormat(getContext(), mCurrentUserId); - if (is24) { - mCalendar.set(Calendar.HOUR_OF_DAY, hh); - } else { - mCalendar.set(Calendar.HOUR, hh); - } - mCalendar.set(Calendar.MINUTE, mm); + // Only registered for COMMAND_CLOCK + String millis = args.getString("millis"); + String hhmm = args.getString("hhmm"); + if (millis != null) { + mCalendar.setTimeInMillis(Long.parseLong(millis)); + } else if (hhmm != null && hhmm.length() == 4) { + int hh = Integer.parseInt(hhmm.substring(0, 2)); + int mm = Integer.parseInt(hhmm.substring(2)); + boolean is24 = DateFormat.is24HourFormat(getContext(), mCurrentUserId); + if (is24) { + mCalendar.set(Calendar.HOUR_OF_DAY, hh); + } else { + mCalendar.set(Calendar.HOUR, hh); } - setText(getSmallTime()); - setContentDescription(mContentDescriptionFormat.format(mCalendar.getTime())); + mCalendar.set(Calendar.MINUTE, mm); } + setText(getSmallTime()); + setContentDescription(mContentDescriptionFormat.format(mCalendar.getTime())); + } + + @Override + public void onDemoModeStarted() { + mDemoMode = true; + } + + @Override + public void onDemoModeFinished() { + mDemoMode = false; + updateClock(); } private final BroadcastReceiver mScreenReceiver = new BroadcastReceiver() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java index 911715fdba63..8207012af6cc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java @@ -21,6 +21,8 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteException; +import androidx.annotation.NonNull; + import java.util.ArrayList; public class DataSaverControllerImpl implements DataSaverController { @@ -41,7 +43,8 @@ public class DataSaverControllerImpl implements DataSaverController { } } - public void addCallback(Listener listener) { + @Override + public void addCallback(@NonNull Listener listener) { synchronized (mListeners) { mListeners.add(listener); if (mListeners.size() == 1) { @@ -51,7 +54,8 @@ public class DataSaverControllerImpl implements DataSaverController { listener.onDataSaverChanged(isDataSaverEnabled()); } - public void removeCallback(Listener listener) { + @Override + public void removeCallback(@NonNull Listener listener) { synchronized (mListeners) { mListeners.remove(listener); if (mListeners.size() == 0) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java index 7280a881655c..9b4e16525df2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java @@ -24,18 +24,20 @@ import android.provider.Settings.Global; import android.provider.Settings.Secure; import android.util.Log; +import androidx.annotation.NonNull; + import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.settings.CurrentUserTracker; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Singleton; /** */ -@Singleton +@SysUISingleton public class DeviceProvisionedControllerImpl extends CurrentUserTracker implements DeviceProvisionedController { @@ -87,7 +89,7 @@ public class DeviceProvisionedControllerImpl extends CurrentUserTracker implemen } @Override - public void addCallback(DeviceProvisionedListener listener) { + public void addCallback(@NonNull DeviceProvisionedListener listener) { mListeners.add(listener); if (mListeners.size() == 1) { startListening(getCurrentUser()); @@ -97,7 +99,7 @@ public class DeviceProvisionedControllerImpl extends CurrentUserTracker implemen } @Override - public void removeCallback(DeviceProvisionedListener listener) { + public void removeCallback(@NonNull DeviceProvisionedListener listener) { mListeners.remove(listener); if (mListeners.size() == 0) { stopListening(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java index eeef726ace75..5011d96d57f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java @@ -19,6 +19,7 @@ import android.content.res.Configuration; import android.os.Handler; import android.util.ArrayMap; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.Plugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.plugins.PluginManager; @@ -34,11 +35,10 @@ import java.util.function.Consumer; import java.util.function.Supplier; import javax.inject.Inject; -import javax.inject.Singleton; /** */ -@Singleton +@SysUISingleton public class ExtensionControllerImpl implements ExtensionController { public static final int SORT_ORDER_PLUGIN = 0; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java index 41ff9d1029b2..d7c2b9664011 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java @@ -30,18 +30,21 @@ import android.provider.Settings.Secure; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.NonNull; + +import com.android.systemui.dagger.SysUISingleton; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Singleton; /** * Manages the flashlight. */ -@Singleton +@SysUISingleton public class FlashlightControllerImpl implements FlashlightController { private static final String TAG = "FlashlightController"; @@ -123,7 +126,8 @@ public class FlashlightControllerImpl implements FlashlightController { return mTorchAvailable; } - public void addCallback(FlashlightListener l) { + @Override + public void addCallback(@NonNull FlashlightListener l) { synchronized (mListeners) { if (mCameraId == null) { tryInitCamera(); @@ -135,7 +139,8 @@ public class FlashlightControllerImpl implements FlashlightController { } } - public void removeCallback(FlashlightListener l) { + @Override + public void removeCallback(@NonNull FlashlightListener l) { synchronized (mListeners) { cleanUpListenersLocked(l); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java index 60ee75b534d8..99feb18b33e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java @@ -30,7 +30,10 @@ import android.os.HandlerExecutor; import android.os.UserManager; import android.util.Log; +import androidx.annotation.NonNull; + import com.android.internal.util.ConcurrentUtils; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -40,12 +43,11 @@ import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controller used to retrieve information related to a hotspot. */ -@Singleton +@SysUISingleton public class HotspotControllerImpl implements HotspotController, WifiManager.SoftApCallback { private static final String TAG = "HotspotController"; @@ -143,7 +145,7 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof * changes. It will immediately trigger the callback added to notify current state. */ @Override - public void addCallback(Callback callback) { + public void addCallback(@NonNull Callback callback) { synchronized (mCallbacks) { if (callback == null || mCallbacks.contains(callback)) return; if (DEBUG) Log.d(TAG, "addCallback " + callback); @@ -163,7 +165,7 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof } @Override - public void removeCallback(Callback callback) { + public void removeCallback(@NonNull Callback callback) { if (callback == null) return; if (DEBUG) Log.d(TAG, "removeCallback " + callback); synchronized (mCallbacks) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java index a7f60d64c332..7f4eec745690 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java @@ -31,6 +31,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -38,11 +39,10 @@ import java.util.ArrayList; import java.util.Objects; import javax.inject.Inject; -import javax.inject.Singleton; /** */ -@Singleton +@SysUISingleton public class KeyguardStateControllerImpl implements KeyguardStateController, Dumpable { private static final boolean DEBUG_AUTH_WITH_ADB = false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java index adfc14e1d72b..0fdc80b3d97a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java @@ -33,12 +33,14 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.systemui.BootCompleteCache; import com.android.systemui.appops.AppOpItem; import com.android.systemui.appops.AppOpsController; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.util.Utils; @@ -47,12 +49,11 @@ import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * A controller to manage changes of location related states and update the views accordingly. */ -@Singleton +@SysUISingleton public class LocationControllerImpl extends BroadcastReceiver implements LocationController, AppOpsController.Callback { @@ -87,12 +88,14 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio /** * Add a callback to listen for changes in location settings. */ - public void addCallback(LocationChangeCallback cb) { + @Override + public void addCallback(@NonNull LocationChangeCallback cb) { mHandler.obtainMessage(H.MSG_ADD_CALLBACK, cb).sendToTarget(); mHandler.sendEmptyMessage(H.MSG_LOCATION_SETTINGS_CHANGED); } - public void removeCallback(LocationChangeCallback cb) { + @Override + public void removeCallback(@NonNull LocationChangeCallback cb) { mHandler.obtainMessage(H.MSG_REMOVE_CALLBACK, cb).sendToTarget(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index 95a97729936b..b790c92b293c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -22,7 +22,7 @@ import android.telephony.SubscriptionInfo; import com.android.settingslib.net.DataUsageController; import com.android.settingslib.wifi.AccessPoint; -import com.android.systemui.DemoMode; +import com.android.systemui.demomode.DemoMode; import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; import java.util.List; @@ -30,8 +30,6 @@ import java.util.List; public interface NetworkController extends CallbackController<SignalCallback>, DemoMode { boolean hasMobileDataFeature(); - void addCallback(SignalCallback cb); - void removeCallback(SignalCallback cb); void setWifiEnabled(boolean enabled); AccessPointController getAccessPointController(); DataUsageController getMobileDataController(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 32c4aec39923..2253ce7a62a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -55,14 +55,18 @@ import android.util.Log; import android.util.MathUtils; import android.util.SparseArray; +import androidx.annotation.NonNull; + import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.net.DataUsageController; -import com.android.systemui.DemoMode; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.demomode.DemoMode; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; @@ -76,10 +80,9 @@ import java.util.List; import java.util.Locale; import javax.inject.Inject; -import javax.inject.Singleton; /** Platform implementation of the network controller. **/ -@Singleton +@SysUISingleton public class NetworkControllerImpl extends BroadcastReceiver implements NetworkController, DemoMode, DataUsageController.NetworkNameProvider, Dumpable { // debug @@ -104,6 +107,7 @@ public class NetworkControllerImpl extends BroadcastReceiver private final DataSaverController mDataSaverController; private final CurrentUserTracker mUserTracker; private final BroadcastDispatcher mBroadcastDispatcher; + private final DemoModeController mDemoModeController; private final Object mLock = new Object(); private Config mConfig; @@ -173,11 +177,16 @@ public class NetworkControllerImpl extends BroadcastReceiver * Construct this controller object and register for updates. */ @Inject - public NetworkControllerImpl(Context context, @Background Looper bgLooper, + public NetworkControllerImpl( + Context context, + @Background Looper bgLooper, DeviceProvisionedController deviceProvisionedController, - BroadcastDispatcher broadcastDispatcher, ConnectivityManager connectivityManager, - TelephonyManager telephonyManager, @Nullable WifiManager wifiManager, - NetworkScoreManager networkScoreManager) { + BroadcastDispatcher broadcastDispatcher, + ConnectivityManager connectivityManager, + TelephonyManager telephonyManager, + @Nullable WifiManager wifiManager, + NetworkScoreManager networkScoreManager, + DemoModeController demoModeController) { this(context, connectivityManager, telephonyManager, wifiManager, @@ -188,7 +197,8 @@ public class NetworkControllerImpl extends BroadcastReceiver new DataUsageController(context), new SubscriptionDefaults(), deviceProvisionedController, - broadcastDispatcher); + broadcastDispatcher, + demoModeController); mReceiverHandler.post(mRegisterListeners); } @@ -202,7 +212,8 @@ public class NetworkControllerImpl extends BroadcastReceiver DataUsageController dataUsageController, SubscriptionDefaults defaultsHandler, DeviceProvisionedController deviceProvisionedController, - BroadcastDispatcher broadcastDispatcher) { + BroadcastDispatcher broadcastDispatcher, + DemoModeController demoModeController) { mContext = context; mConfig = config; mReceiverHandler = new Handler(bgLooper); @@ -215,6 +226,7 @@ public class NetworkControllerImpl extends BroadcastReceiver mConnectivityManager = connectivityManager; mHasMobileDataFeature = mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); + mDemoModeController = demoModeController; // telephony mPhone = telephonyManager; @@ -306,6 +318,8 @@ public class NetworkControllerImpl extends BroadcastReceiver doUpdateMobileControllers(); } }; + + mDemoModeController.addCallback(this); } private final Runnable mClearForceValidated = () -> { @@ -514,7 +528,8 @@ public class NetworkControllerImpl extends BroadcastReceiver mCallbackHandler.setEmergencyCallsOnly(mIsEmergency); } - public void addCallback(SignalCallback cb) { + @Override + public void addCallback(@NonNull SignalCallback cb) { cb.setSubs(mCurrentSubscriptions); cb.setIsAirplaneMode(new IconState(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext)); @@ -529,7 +544,7 @@ public class NetworkControllerImpl extends BroadcastReceiver } @Override - public void removeCallback(SignalCallback cb) { + public void removeCallback(@NonNull SignalCallback cb) { mCallbackHandler.setListening(cb, false); } @@ -932,205 +947,217 @@ public class NetworkControllerImpl extends BroadcastReceiver return "UNKNOWN_SOURCE"; } - private boolean mDemoMode; private boolean mDemoInetCondition; private WifiSignalController.WifiState mDemoWifiState; @Override + public void onDemoModeStarted() { + if (DEBUG) Log.d(TAG, "Entering demo mode"); + unregisterListeners(); + mDemoInetCondition = mInetCondition; + mDemoWifiState = mWifiSignalController.getState(); + mDemoWifiState.ssid = "DemoMode"; + } + + @Override + public void onDemoModeFinished() { + if (DEBUG) Log.d(TAG, "Exiting demo mode"); + // Update what MobileSignalControllers, because they may change + // to set the number of sim slots. + updateMobileControllers(); + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController controller = mMobileSignalControllers.valueAt(i); + controller.resetLastState(); + } + mWifiSignalController.resetLastState(); + mReceiverHandler.post(mRegisterListeners); + notifyAllListeners(); + } + + @Override public void dispatchDemoCommand(String command, Bundle args) { - if (!mDemoMode && command.equals(COMMAND_ENTER)) { - if (DEBUG) Log.d(TAG, "Entering demo mode"); - unregisterListeners(); - mDemoMode = true; - mDemoInetCondition = mInetCondition; - mDemoWifiState = mWifiSignalController.getState(); - mDemoWifiState.ssid = "DemoMode"; - } else if (mDemoMode && command.equals(COMMAND_EXIT)) { - if (DEBUG) Log.d(TAG, "Exiting demo mode"); - mDemoMode = false; - // Update what MobileSignalControllers, because they may change - // to set the number of sim slots. - updateMobileControllers(); + if (!mDemoModeController.isInDemoMode()) { + return; + } + + String airplane = args.getString("airplane"); + if (airplane != null) { + boolean show = airplane.equals("show"); + mCallbackHandler.setIsAirplaneMode(new IconState(show, + TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, + mContext)); + } + String fully = args.getString("fully"); + if (fully != null) { + mDemoInetCondition = Boolean.parseBoolean(fully); + BitSet connected = new BitSet(); + + if (mDemoInetCondition) { + connected.set(mWifiSignalController.mTransportType); + } + mWifiSignalController.updateConnectivity(connected, connected); for (int i = 0; i < mMobileSignalControllers.size(); i++) { MobileSignalController controller = mMobileSignalControllers.valueAt(i); - controller.resetLastState(); - } - mWifiSignalController.resetLastState(); - mReceiverHandler.post(mRegisterListeners); - notifyAllListeners(); - } else if (mDemoMode && command.equals(COMMAND_NETWORK)) { - String airplane = args.getString("airplane"); - if (airplane != null) { - boolean show = airplane.equals("show"); - mCallbackHandler.setIsAirplaneMode(new IconState(show, - TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, - mContext)); - } - String fully = args.getString("fully"); - if (fully != null) { - mDemoInetCondition = Boolean.parseBoolean(fully); - BitSet connected = new BitSet(); - if (mDemoInetCondition) { - connected.set(mWifiSignalController.mTransportType); - } - mWifiSignalController.updateConnectivity(connected, connected); - for (int i = 0; i < mMobileSignalControllers.size(); i++) { - MobileSignalController controller = mMobileSignalControllers.valueAt(i); - if (mDemoInetCondition) { - connected.set(controller.mTransportType); - } - controller.updateConnectivity(connected, connected); + connected.set(controller.mTransportType); } + controller.updateConnectivity(connected, connected); } - String wifi = args.getString("wifi"); - if (wifi != null) { - boolean show = wifi.equals("show"); - String level = args.getString("level"); - if (level != null) { - mDemoWifiState.level = level.equals("null") ? -1 - : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1); - mDemoWifiState.connected = mDemoWifiState.level >= 0; - } - String activity = args.getString("activity"); - if (activity != null) { - switch (activity) { - case "inout": - mWifiSignalController.setActivity(DATA_ACTIVITY_INOUT); - break; - case "in": - mWifiSignalController.setActivity(DATA_ACTIVITY_IN); - break; - case "out": - mWifiSignalController.setActivity(DATA_ACTIVITY_OUT); - break; - default: - mWifiSignalController.setActivity(DATA_ACTIVITY_NONE); - break; - } - } else { - mWifiSignalController.setActivity(DATA_ACTIVITY_NONE); - } - String ssid = args.getString("ssid"); - if (ssid != null) { - mDemoWifiState.ssid = ssid; - } - mDemoWifiState.enabled = show; - mWifiSignalController.notifyListeners(); + } + String wifi = args.getString("wifi"); + if (wifi != null) { + boolean show = wifi.equals("show"); + String level = args.getString("level"); + if (level != null) { + mDemoWifiState.level = level.equals("null") ? -1 + : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1); + mDemoWifiState.connected = mDemoWifiState.level >= 0; } - String sims = args.getString("sims"); - if (sims != null) { - int num = MathUtils.constrain(Integer.parseInt(sims), 1, 8); - List<SubscriptionInfo> subs = new ArrayList<>(); - if (num != mMobileSignalControllers.size()) { - mMobileSignalControllers.clear(); - int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax(); - for (int i = start /* get out of normal index range */; i < start + num; i++) { - subs.add(addSignalController(i, i)); - } - mCallbackHandler.setSubs(subs); - for (int i = 0; i < mMobileSignalControllers.size(); i++) { - int key = mMobileSignalControllers.keyAt(i); - MobileSignalController controller = mMobileSignalControllers.get(key); - controller.notifyListeners(); - } + String activity = args.getString("activity"); + if (activity != null) { + switch (activity) { + case "inout": + mWifiSignalController.setActivity(DATA_ACTIVITY_INOUT); + break; + case "in": + mWifiSignalController.setActivity(DATA_ACTIVITY_IN); + break; + case "out": + mWifiSignalController.setActivity(DATA_ACTIVITY_OUT); + break; + default: + mWifiSignalController.setActivity(DATA_ACTIVITY_NONE); + break; } + } else { + mWifiSignalController.setActivity(DATA_ACTIVITY_NONE); } - String nosim = args.getString("nosim"); - if (nosim != null) { - mHasNoSubs = nosim.equals("show"); - mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected); + String ssid = args.getString("ssid"); + if (ssid != null) { + mDemoWifiState.ssid = ssid; } - String mobile = args.getString("mobile"); - if (mobile != null) { - boolean show = mobile.equals("show"); - String datatype = args.getString("datatype"); - String slotString = args.getString("slot"); - int slot = TextUtils.isEmpty(slotString) ? 0 : Integer.parseInt(slotString); - slot = MathUtils.constrain(slot, 0, 8); - // Ensure we have enough sim slots - List<SubscriptionInfo> subs = new ArrayList<>(); - while (mMobileSignalControllers.size() <= slot) { - int nextSlot = mMobileSignalControllers.size(); - subs.add(addSignalController(nextSlot, nextSlot)); - } - if (!subs.isEmpty()) { - mCallbackHandler.setSubs(subs); - } - // Hack to index linearly for easy use. - MobileSignalController controller = mMobileSignalControllers.valueAt(slot); - controller.getState().dataSim = datatype != null; - controller.getState().isDefault = datatype != null; - controller.getState().dataConnected = datatype != null; - if (datatype != null) { - controller.getState().iconGroup = - datatype.equals("1x") ? TelephonyIcons.ONE_X : - datatype.equals("3g") ? TelephonyIcons.THREE_G : - datatype.equals("4g") ? TelephonyIcons.FOUR_G : - datatype.equals("4g+") ? TelephonyIcons.FOUR_G_PLUS : - datatype.equals("5g") ? TelephonyIcons.NR_5G : - datatype.equals("5ge") ? TelephonyIcons.LTE_CA_5G_E : - datatype.equals("5g+") ? TelephonyIcons.NR_5G_PLUS : - datatype.equals("e") ? TelephonyIcons.E : - datatype.equals("g") ? TelephonyIcons.G : - datatype.equals("h") ? TelephonyIcons.H : - datatype.equals("h+") ? TelephonyIcons.H_PLUS : - datatype.equals("lte") ? TelephonyIcons.LTE : - datatype.equals("lte+") ? TelephonyIcons.LTE_PLUS : - datatype.equals("dis") ? TelephonyIcons.DATA_DISABLED : - datatype.equals("not") ? TelephonyIcons.NOT_DEFAULT_DATA : - TelephonyIcons.UNKNOWN; - } - if (args.containsKey("roam")) { - controller.getState().roaming = "show".equals(args.getString("roam")); - } - String level = args.getString("level"); - if (level != null) { - controller.getState().level = level.equals("null") ? -1 - : Math.min(Integer.parseInt(level), - CellSignalStrength.getNumSignalStrengthLevels()); - controller.getState().connected = controller.getState().level >= 0; - } - if (args.containsKey("inflate")) { - for (int i = 0; i < mMobileSignalControllers.size(); i++) { - mMobileSignalControllers.valueAt(i).mInflateSignalStrengths = - "true".equals(args.getString("inflate")); - } + mDemoWifiState.enabled = show; + mWifiSignalController.notifyListeners(); + } + String sims = args.getString("sims"); + if (sims != null) { + int num = MathUtils.constrain(Integer.parseInt(sims), 1, 8); + List<SubscriptionInfo> subs = new ArrayList<>(); + if (num != mMobileSignalControllers.size()) { + mMobileSignalControllers.clear(); + int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax(); + for (int i = start /* get out of normal index range */; i < start + num; i++) { + subs.add(addSignalController(i, i)); } - String activity = args.getString("activity"); - if (activity != null) { - controller.getState().dataConnected = true; - switch (activity) { - case "inout": - controller.setActivity(TelephonyManager.DATA_ACTIVITY_INOUT); - break; - case "in": - controller.setActivity(TelephonyManager.DATA_ACTIVITY_IN); - break; - case "out": - controller.setActivity(TelephonyManager.DATA_ACTIVITY_OUT); - break; - default: - controller.setActivity(TelephonyManager.DATA_ACTIVITY_NONE); - break; - } - } else { - controller.setActivity(TelephonyManager.DATA_ACTIVITY_NONE); + mCallbackHandler.setSubs(subs); + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + int key = mMobileSignalControllers.keyAt(i); + MobileSignalController controller = mMobileSignalControllers.get(key); + controller.notifyListeners(); } - controller.getState().enabled = show; - controller.notifyListeners(); } - String carrierNetworkChange = args.getString("carriernetworkchange"); - if (carrierNetworkChange != null) { - boolean show = carrierNetworkChange.equals("show"); + } + String nosim = args.getString("nosim"); + if (nosim != null) { + mHasNoSubs = nosim.equals("show"); + mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected); + } + String mobile = args.getString("mobile"); + if (mobile != null) { + boolean show = mobile.equals("show"); + String datatype = args.getString("datatype"); + String slotString = args.getString("slot"); + int slot = TextUtils.isEmpty(slotString) ? 0 : Integer.parseInt(slotString); + slot = MathUtils.constrain(slot, 0, 8); + // Ensure we have enough sim slots + List<SubscriptionInfo> subs = new ArrayList<>(); + while (mMobileSignalControllers.size() <= slot) { + int nextSlot = mMobileSignalControllers.size(); + subs.add(addSignalController(nextSlot, nextSlot)); + } + if (!subs.isEmpty()) { + mCallbackHandler.setSubs(subs); + } + // Hack to index linearly for easy use. + MobileSignalController controller = mMobileSignalControllers.valueAt(slot); + controller.getState().dataSim = datatype != null; + controller.getState().isDefault = datatype != null; + controller.getState().dataConnected = datatype != null; + if (datatype != null) { + controller.getState().iconGroup = + datatype.equals("1x") ? TelephonyIcons.ONE_X : + datatype.equals("3g") ? TelephonyIcons.THREE_G : + datatype.equals("4g") ? TelephonyIcons.FOUR_G : + datatype.equals("4g+") ? TelephonyIcons.FOUR_G_PLUS : + datatype.equals("5g") ? TelephonyIcons.NR_5G : + datatype.equals("5ge") ? TelephonyIcons.LTE_CA_5G_E : + datatype.equals("5g+") ? TelephonyIcons.NR_5G_PLUS : + datatype.equals("e") ? TelephonyIcons.E : + datatype.equals("g") ? TelephonyIcons.G : + datatype.equals("h") ? TelephonyIcons.H : + datatype.equals("h+") ? TelephonyIcons.H_PLUS : + datatype.equals("lte") ? TelephonyIcons.LTE : + datatype.equals("lte+") ? TelephonyIcons.LTE_PLUS : + datatype.equals("dis") ? TelephonyIcons.DATA_DISABLED : + datatype.equals("not") ? TelephonyIcons.NOT_DEFAULT_DATA : + TelephonyIcons.UNKNOWN; + } + if (args.containsKey("roam")) { + controller.getState().roaming = "show".equals(args.getString("roam")); + } + String level = args.getString("level"); + if (level != null) { + controller.getState().level = level.equals("null") ? -1 + : Math.min(Integer.parseInt(level), + CellSignalStrength.getNumSignalStrengthLevels()); + controller.getState().connected = controller.getState().level >= 0; + } + if (args.containsKey("inflate")) { for (int i = 0; i < mMobileSignalControllers.size(); i++) { - MobileSignalController controller = mMobileSignalControllers.valueAt(i); - controller.setCarrierNetworkChangeMode(show); + mMobileSignalControllers.valueAt(i).mInflateSignalStrengths = + "true".equals(args.getString("inflate")); } } + String activity = args.getString("activity"); + if (activity != null) { + controller.getState().dataConnected = true; + switch (activity) { + case "inout": + controller.setActivity(TelephonyManager.DATA_ACTIVITY_INOUT); + break; + case "in": + controller.setActivity(TelephonyManager.DATA_ACTIVITY_IN); + break; + case "out": + controller.setActivity(TelephonyManager.DATA_ACTIVITY_OUT); + break; + default: + controller.setActivity(TelephonyManager.DATA_ACTIVITY_NONE); + break; + } + } else { + controller.setActivity(TelephonyManager.DATA_ACTIVITY_NONE); + } + controller.getState().enabled = show; + controller.notifyListeners(); + } + String carrierNetworkChange = args.getString("carriernetworkchange"); + if (carrierNetworkChange != null) { + boolean show = carrierNetworkChange.equals("show"); + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController controller = mMobileSignalControllers.valueAt(i); + controller.setCarrierNetworkChangeMode(show); + } } } + @Override + public List<String> demoCommands() { + List<String> s = new ArrayList<>(); + s.add(DemoMode.COMMAND_NETWORK); + return s; + } + private SubscriptionInfo addSignalController(int id, int simSlotIndex) { SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0, null, null, null, "", false, null, null); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java index 288b3aff2af6..272c494b1af4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java @@ -23,17 +23,20 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.UserHandle; +import androidx.annotation.NonNull; + +import com.android.systemui.dagger.SysUISingleton; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Singleton; /** * Implementation of {@link NextAlarmController} */ -@Singleton +@SysUISingleton public class NextAlarmControllerImpl extends BroadcastReceiver implements NextAlarmController { @@ -59,12 +62,14 @@ public class NextAlarmControllerImpl extends BroadcastReceiver pw.print(" mNextAlarm="); pw.println(mNextAlarm); } - public void addCallback(NextAlarmChangeCallback cb) { + @Override + public void addCallback(@NonNull NextAlarmChangeCallback cb) { mChangeCallbacks.add(cb); cb.onNextAlarmChanged(mNextAlarm); } - public void removeCallback(NextAlarmChangeCallback cb) { + @Override + public void removeCallback(@NonNull NextAlarmChangeCallback cb) { mChangeCallbacks.remove(cb); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java index 7ef9945b4d0e..ac8b47dc7e0b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java @@ -21,17 +21,17 @@ import android.content.Context; import android.content.res.Configuration; import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.qs.QSFragment; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.StatusBar; import javax.inject.Inject; -import javax.inject.Singleton; /** * Let {@link RemoteInputView} to control the visibility of QuickSetting. */ -@Singleton +@SysUISingleton public class RemoteInputQuickSettingsDisabler implements ConfigurationController.ConfigurationListener { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputUriController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputUriController.java index b5031832adc5..03b6122102c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputUriController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputUriController.java @@ -23,17 +23,17 @@ import android.util.Log; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import javax.inject.Inject; -import javax.inject.Singleton; /** * Handles granting and revoking inline URI grants associated with RemoteInputs. */ -@Singleton +@SysUISingleton public class RemoteInputUriController { private final IStatusBarService mStatusBarManagerService; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java index 1f368e164678..53d68d0ff0ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java @@ -19,15 +19,17 @@ package com.android.systemui.statusbar.policy; import android.content.Context; import android.os.UserHandle; +import androidx.annotation.NonNull; + import com.android.internal.view.RotationPolicy; +import com.android.systemui.dagger.SysUISingleton; import java.util.concurrent.CopyOnWriteArrayList; import javax.inject.Inject; -import javax.inject.Singleton; /** Platform implementation of the rotation lock controller. **/ -@Singleton +@SysUISingleton public final class RotationLockControllerImpl implements RotationLockController { private final Context mContext; private final CopyOnWriteArrayList<RotationLockControllerCallback> mCallbacks = @@ -47,12 +49,14 @@ public final class RotationLockControllerImpl implements RotationLockController setListening(true); } - public void addCallback(RotationLockControllerCallback callback) { + @Override + public void addCallback(@NonNull RotationLockControllerCallback callback) { mCallbacks.add(callback); notifyChanged(callback); } - public void removeCallback(RotationLockControllerCallback callback) { + @Override + public void removeCallback(@NonNull RotationLockControllerCallback callback) { mCallbacks.remove(callback); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index 309d4b04ebbf..7e54e8d1c1c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -42,11 +42,14 @@ import android.util.Log; import android.util.Pair; import android.util.SparseArray; +import androidx.annotation.NonNull; + import com.android.internal.annotations.GuardedBy; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.settings.CurrentUserTracker; @@ -56,11 +59,10 @@ import java.util.ArrayList; import java.util.concurrent.Executor; import javax.inject.Inject; -import javax.inject.Singleton; /** */ -@Singleton +@SysUISingleton public class SecurityControllerImpl extends CurrentUserTracker implements SecurityController { private static final String TAG = "SecurityController"; @@ -274,7 +276,7 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi } @Override - public void removeCallback(SecurityControllerCallback callback) { + public void removeCallback(@NonNull SecurityControllerCallback callback) { synchronized (mCallbacks) { if (callback == null) return; if (DEBUG) Log.d(TAG, "removeCallback " + callback); @@ -283,7 +285,7 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi } @Override - public void addCallback(SecurityControllerCallback callback) { + public void addCallback(@NonNull SecurityControllerCallback callback) { synchronized (mCallbacks) { if (callback == null || mCallbacks.contains(callback)) return; if (DEBUG) Log.d(TAG, "addCallback " + callback); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java index 5db66932d3c1..20cc46ff6bbd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java @@ -19,16 +19,19 @@ package com.android.systemui.statusbar.policy; import android.content.Context; import android.hardware.SensorPrivacyManager; +import androidx.annotation.NonNull; + +import com.android.systemui.dagger.SysUISingleton; + import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controls sensor privacy state and notification. */ -@Singleton +@SysUISingleton public class SensorPrivacyControllerImpl implements SensorPrivacyController, SensorPrivacyManager.OnSensorPrivacyChangedListener { private SensorPrivacyManager mSensorPrivacyManager; @@ -60,7 +63,7 @@ public class SensorPrivacyControllerImpl implements SensorPrivacyController, /** * Adds the provided listener for callbacks when sensor privacy state changes. */ - public void addCallback(OnSensorPrivacyChangedListener listener) { + public void addCallback(@NonNull OnSensorPrivacyChangedListener listener) { synchronized (mLock) { mListeners.add(listener); notifyListenerLocked(listener); @@ -70,7 +73,7 @@ public class SensorPrivacyControllerImpl implements SensorPrivacyController, /** * Removes the provided listener from callbacks when sensor privacy state changes. */ - public void removeCallback(OnSensorPrivacyChangedListener listener) { + public void removeCallback(@NonNull OnSensorPrivacyChangedListener listener) { synchronized (mLock) { mListeners.remove(listener); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java index 311e873812ac..52a6bcaa6753 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java @@ -27,13 +27,13 @@ import android.util.Log; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.util.DeviceConfigProxy; import javax.inject.Inject; -import javax.inject.Singleton; -@Singleton +@SysUISingleton public final class SmartReplyConstants { private static final String TAG = "SmartReplyConstants"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java index 0ca6ff6ec66e..9eaee22b54ec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java @@ -34,18 +34,20 @@ import android.os.UserManager; import android.provider.ContactsContract; import android.util.Log; +import androidx.annotation.NonNull; + import com.android.internal.util.UserIcons; import com.android.settingslib.drawable.UserIconDrawable; import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Singleton; /** */ -@Singleton +@SysUISingleton public class UserInfoControllerImpl implements UserInfoController { private static final String TAG = "UserInfoController"; @@ -75,12 +77,14 @@ public class UserInfoControllerImpl implements UserInfoController { null, null); } - public void addCallback(OnUserInfoChangedListener callback) { + @Override + public void addCallback(@NonNull OnUserInfoChangedListener callback) { mCallbacks.add(callback); callback.onUserInfoChanged(mUserName, mUserDrawable, mUserAccount); } - public void removeCallback(OnUserInfoChangedListener callback) { + @Override + public void removeCallback(@NonNull OnUserInfoChangedListener callback) { mCallbacks.remove(callback); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index ce5bb0508c0a..f9ac760a3367 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -62,6 +62,7 @@ import com.android.systemui.Prefs.Key; import com.android.systemui.R; import com.android.systemui.SystemUISecondaryUserService; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.DetailAdapter; @@ -76,12 +77,11 @@ import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * Keeps a list of all users on the device for user switching. */ -@Singleton +@SysUISingleton public class UserSwitcherController implements Dumpable { public static final float USER_SWITCH_ENABLED_ALPHA = 1.0f; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java index 5257ce4c6bd9..4ae96651b570 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java @@ -84,7 +84,7 @@ public class WifiSignalController extends R.bool.config_showWifiIndicatorWhenEnabled); boolean wifiVisible = mCurrentState.enabled && ( (mCurrentState.connected && mCurrentState.inetCondition == 1) - || !mHasMobileDataFeature || mWifiTracker.isDefaultNetwork + || !mHasMobileDataFeature || mCurrentState.isDefault || visibleWhenEnabled); String wifiDesc = mCurrentState.connected ? mCurrentState.ssid : null; boolean ssidPresent = wifiVisible && mCurrentState.ssid != null; @@ -107,6 +107,7 @@ public class WifiSignalController extends public void fetchInitialState() { mWifiTracker.fetchInitialState(); mCurrentState.enabled = mWifiTracker.enabled; + mCurrentState.isDefault = mWifiTracker.isDefaultNetwork; mCurrentState.connected = mWifiTracker.connected; mCurrentState.ssid = mWifiTracker.ssid; mCurrentState.rssi = mWifiTracker.rssi; @@ -121,6 +122,7 @@ public class WifiSignalController extends public void handleBroadcast(Intent intent) { mWifiTracker.handleBroadcast(intent); mCurrentState.enabled = mWifiTracker.enabled; + mCurrentState.isDefault = mWifiTracker.isDefaultNetwork; mCurrentState.connected = mWifiTracker.connected; mCurrentState.ssid = mWifiTracker.ssid; mCurrentState.rssi = mWifiTracker.rssi; @@ -131,6 +133,7 @@ public class WifiSignalController extends private void handleStatusUpdated() { mCurrentState.statusLabel = mWifiTracker.statusLabel; + mCurrentState.isDefault = mWifiTracker.isDefaultNetwork; notifyListenersIfNecessary(); } @@ -156,6 +159,7 @@ public class WifiSignalController extends static class WifiState extends SignalController.State { String ssid; boolean isTransient; + boolean isDefault; String statusLabel; @Override @@ -164,6 +168,7 @@ public class WifiSignalController extends WifiState state = (WifiState) s; ssid = state.ssid; isTransient = state.isTransient; + isDefault = state.isDefault; statusLabel = state.statusLabel; } @@ -172,6 +177,7 @@ public class WifiSignalController extends super.toString(builder); builder.append(",ssid=").append(ssid) .append(",isTransient=").append(isTransient) + .append(",isDefault=").append(isDefault) .append(",statusLabel=").append(statusLabel); } @@ -183,6 +189,7 @@ public class WifiSignalController extends WifiState other = (WifiState) o; return Objects.equals(other.ssid, ssid) && other.isTransient == isTransient + && other.isDefault == isDefault && TextUtils.equals(other.statusLabel, statusLabel); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java index 4376a0145826..897a3b863e73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java @@ -37,9 +37,12 @@ import android.service.notification.ZenModeConfig.ZenRule; import android.text.format.DateFormat; import android.util.Log; +import androidx.annotation.NonNull; + import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dumpable; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.qs.GlobalSetting; import com.android.systemui.settings.CurrentUserTracker; @@ -51,10 +54,9 @@ import java.util.ArrayList; import java.util.Objects; import javax.inject.Inject; -import javax.inject.Singleton; /** Platform implementation of the zen mode controller. **/ -@Singleton +@SysUISingleton public class ZenModeControllerImpl extends CurrentUserTracker implements ZenModeController, Dumpable { private static final String TAG = "ZenModeController"; @@ -124,14 +126,14 @@ public class ZenModeControllerImpl extends CurrentUserTracker } @Override - public void addCallback(Callback callback) { + public void addCallback(@NonNull Callback callback) { synchronized (mCallbacksLock) { mCallbacks.add(callback); } } @Override - public void removeCallback(Callback callback) { + public void removeCallback(@NonNull Callback callback) { synchronized (mCallbacksLock) { mCallbacks.remove(callback); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index c0602762ef29..bcfff60dadf3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -29,11 +29,11 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.assist.AssistManager; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; @@ -46,7 +46,7 @@ import dagger.Lazy; * recording, discloses the responsible applications </li> * </ul> */ -@Singleton +@SysUISingleton public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks { private static final String ACTION_OPEN_TV_NOTIFICATIONS_PANEL = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java index e3eed35f0483..a29db4d98329 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.tv.micdisclosure; +import static android.provider.DeviceConfig.NAMESPACE_PRIVACY; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import android.animation.Animator; @@ -25,11 +26,8 @@ import android.animation.ObjectAnimator; import android.annotation.IntDef; import android.annotation.UiThread; import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.database.ContentObserver; import android.graphics.PixelFormat; -import android.provider.Settings; +import android.provider.DeviceConfig; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; @@ -46,8 +44,8 @@ import com.android.systemui.statusbar.tv.TvStatusBar; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; -import java.util.LinkedList; -import java.util.Queue; +import java.util.Collections; +import java.util.List; import java.util.Set; /** @@ -65,9 +63,9 @@ public class AudioRecordingDisclosureBar implements // CtsSystemUiHostTestCases:TvMicrophoneCaptureIndicatorTest private static final String LAYOUT_PARAMS_TITLE = "MicrophoneCaptureIndicator"; - private static final String ENABLE_FLAG = "sysui_mic_disclosure_enable"; - private static final String EXEMPT_PACKAGES_LIST = "sysui_mic_disclosure_exempt"; - private static final String FORCED_PACKAGES_LIST = "sysui_mic_disclosure_forced"; + private static final String ENABLED_FLAG = "mic_disclosure_enabled"; + private static final String EXEMPT_PACKAGES_LIST = "mic_disclosure_exempt_packages"; + private static final String FORCED_PACKAGES_LIST = "mic_disclosure_forced_packages"; @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"STATE_"}, value = { @@ -75,9 +73,6 @@ public class AudioRecordingDisclosureBar implements STATE_NOT_SHOWN, STATE_APPEARING, STATE_SHOWN, - STATE_MINIMIZING, - STATE_MINIMIZED, - STATE_MAXIMIZING, STATE_DISAPPEARING }) public @interface State {} @@ -86,16 +81,12 @@ public class AudioRecordingDisclosureBar implements private static final int STATE_NOT_SHOWN = 0; private static final int STATE_APPEARING = 1; private static final int STATE_SHOWN = 2; - private static final int STATE_MINIMIZING = 3; - private static final int STATE_MINIMIZED = 4; - private static final int STATE_MAXIMIZING = 5; - private static final int STATE_DISAPPEARING = 6; + private static final int STATE_DISAPPEARING = 3; private static final int ANIMATION_DURATION = 600; - private static final int MAXIMIZED_DURATION = 3000; private final Context mContext; - private boolean mIsEnabledInSettings; + private boolean mIsEnabled; private View mIndicatorView; private View mIconTextsContainer; @@ -114,53 +105,38 @@ public class AudioRecordingDisclosureBar implements */ private AudioActivityObserver[] mAudioActivityObservers; /** - * Whether the indicator should expand and show the recording application's label. - * If disabled ({@code false}) the "minimized" ({@link #STATE_MINIMIZED}) indicator would appear - * on the screen whenever an application is recording, but will not reveal to the user what - * application this is. - */ - private final boolean mRevealRecordingPackages; - /** - * Set of applications that we've notified the user about since the indicator came up. Meaning - * that if an application is in this list then at some point since the indicator came up, it - * was expanded showing this application's title. - * Used not to notify the user about the same application again while the indicator is shown. - * We empty this set every time the indicator goes off the screen (we always call {@code - * mSessionNotifiedPackages.clear()} before calling {@link #hide()}). - */ - private final Set<String> mSessionNotifiedPackages = new ArraySet<>(); - /** - * If an application starts recording while the TV indicator is neither in {@link - * #STATE_NOT_SHOWN} nor in {@link #STATE_MINIMIZED}, then we add the application's package - * name to the queue, from which we take packages names one by one to disclose the - * corresponding applications' titles to the user, whenever the indicator eventually comes to - * one of the two aforementioned states. - */ - private final Queue<String> mPendingNotificationPackages = new LinkedList<>(); - /** * Set of applications for which we make an exception and do not show the indicator. This gets * populated once - in {@link #AudioRecordingDisclosureBar(Context)}. */ - private final Set<String> mExemptPackages; + private final Set<String> mExemptPackages = new ArraySet<>(); public AudioRecordingDisclosureBar(Context context) { mContext = context; - // Loading configs - mRevealRecordingPackages = mContext.getResources().getBoolean( - R.bool.audio_recording_disclosure_reveal_packages); - mExemptPackages = new ArraySet<>( - Arrays.asList(mContext.getResources().getStringArray( - R.array.audio_recording_disclosure_exempt_apps))); - mExemptPackages.addAll(Arrays.asList(getGlobalStringArray(EXEMPT_PACKAGES_LIST))); - mExemptPackages.removeAll(Arrays.asList(getGlobalStringArray(FORCED_PACKAGES_LIST))); - - // Check setting, and start if enabled - mIsEnabledInSettings = checkIfEnabledInSettings(); - registerSettingsObserver(); - if (mIsEnabledInSettings) { + // Load configs + reloadExemptPackages(); + + mIsEnabled = DeviceConfig.getBoolean(NAMESPACE_PRIVACY, ENABLED_FLAG, true); + // Start if enabled + if (mIsEnabled) { start(); } + + // Set up a config change listener + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_PRIVACY, mContext.getMainExecutor(), + mConfigChangeListener); + } + + private void reloadExemptPackages() { + mExemptPackages.clear(); + mExemptPackages.addAll(Arrays.asList(mContext.getResources().getStringArray( + R.array.audio_recording_disclosure_exempt_apps))); + mExemptPackages.addAll( + splitByComma( + DeviceConfig.getString(NAMESPACE_PRIVACY, EXEMPT_PACKAGES_LIST, null))); + mExemptPackages.removeAll( + splitByComma( + DeviceConfig.getString(NAMESPACE_PRIVACY, FORCED_PACKAGES_LIST, null))); } @UiThread @@ -197,10 +173,6 @@ public class AudioRecordingDisclosureBar implements if (mState != STATE_NOT_SHOWN) { removeIndicatorView(); } - - // Clean up the state. - mSessionNotifiedPackages.clear(); - mPendingNotificationPackages.clear(); } @UiThread @@ -218,68 +190,29 @@ public class AudioRecordingDisclosureBar implements } if (active) { - showIndicatorForPackageIfNeeded(packageName); + showIfNotShown(); } else { hideIndicatorIfNeeded(); } } @UiThread - private void showIndicatorForPackageIfNeeded(String packageName) { - if (DEBUG) Log.d(TAG, "showIndicatorForPackageIfNeeded, packageName=" + packageName); - if (!mSessionNotifiedPackages.add(packageName)) { - // We've already notified user about this app, no need to do it again. - if (DEBUG) Log.d(TAG, " - already notified"); - return; - } - - switch (mState) { - case STATE_NOT_SHOWN: - show(packageName); - break; - - case STATE_MINIMIZED: - if (mRevealRecordingPackages) { - expand(packageName); - } - break; - - case STATE_DISAPPEARING: - case STATE_APPEARING: - case STATE_MAXIMIZING: - case STATE_SHOWN: - case STATE_MINIMIZING: - // Currently animating or expanded. Thus add to the pending notifications, and it - // will be picked up once the indicator comes to the STATE_MINIMIZED. - mPendingNotificationPackages.add(packageName); - break; - } - } - - @UiThread private void hideIndicatorIfNeeded() { - // If not MINIMIZED, will check whether the indicator should be hidden when the indicator - // comes to the STATE_MINIMIZED eventually. - if (mState != STATE_MINIMIZED) return; - - // If is in the STATE_MINIMIZED, but there are other active recorders - simply ignore. - for (int index = mAudioActivityObservers.length - 1; index >= 0; index--) { - for (String activePackage : mAudioActivityObservers[index].getActivePackages()) { - if (mExemptPackages.contains(activePackage)) continue; - return; - } + // If not STATE_APPEARING, will check whether the indicator should be hidden when the + // indicator comes to the STATE_SHOWN. + // If STATE_DISAPPEARING or STATE_SHOWN - nothing else for us to do here. + if (mState != STATE_SHOWN) return; + + // If is in the STATE_SHOWN and there are no active recorders - hide. + if (!hasActiveRecorders()) { + hide(); } - - // Clear the state and hide the indicator. - mSessionNotifiedPackages.clear(); - hide(); } @UiThread - private void show(String packageName) { - if (DEBUG) { - Log.d(TAG, "Showing indicator"); - } + private void showIfNotShown() { + if (mState != STATE_NOT_SHOWN) return; + if (DEBUG) Log.d(TAG, "Showing indicator"); mIsLtr = mContext.getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; @@ -295,30 +228,14 @@ public class AudioRecordingDisclosureBar implements mTextView = mTextsContainers.findViewById(R.id.text); mBgEnd = mIndicatorView.findViewById(R.id.bg_end); - // Set up the notification text - if (mRevealRecordingPackages) { - // Swap background drawables depending on layout directions (both drawables have rounded - // corners only on one side) - if (mIsLtr) { - mBgEnd.setBackgroundResource(R.drawable.tv_rect_dark_right_rounded); - mIconContainerBg.setBackgroundResource(R.drawable.tv_rect_dark_left_rounded); - } else { - mBgEnd.setBackgroundResource(R.drawable.tv_rect_dark_left_rounded); - mIconContainerBg.setBackgroundResource(R.drawable.tv_rect_dark_right_rounded); - } - - final String label = getApplicationLabel(packageName); - mTextView.setText(mContext.getString(R.string.app_accessed_mic, label)); - } else { - mTextsContainers.setVisibility(View.GONE); - mIconContainerBg.setVisibility(View.GONE); - mTextView.setVisibility(View.GONE); - mBgEnd.setVisibility(View.GONE); - mTextsContainers = null; - mIconContainerBg = null; - mTextView = null; - mBgEnd = null; - } + mTextsContainers.setVisibility(View.GONE); + mIconContainerBg.setVisibility(View.GONE); + mTextView.setVisibility(View.GONE); + mBgEnd.setVisibility(View.GONE); + mTextsContainers = null; + mIconContainerBg = null; + mTextView = null; + mBgEnd = null; // Initially change the visibility to INVISIBLE, wait until and receives the size and // then animate it moving from "off" the screen correctly @@ -353,9 +270,7 @@ public class AudioRecordingDisclosureBar implements @Override public void onAnimationStart(Animator animation, boolean isReverse) { - if (mState == STATE_STOPPED) { - return; - } + if (mState == STATE_STOPPED) return; // Indicator is INVISIBLE at the moment, change it. mIndicatorView.setVisibility(View.VISIBLE); @@ -363,11 +278,7 @@ public class AudioRecordingDisclosureBar implements @Override public void onAnimationEnd(Animator animation) { - if (mRevealRecordingPackages) { - onExpanded(); - } else { - onMinimized(); - } + onAppeared(); } }); set.start(); @@ -391,60 +302,9 @@ public class AudioRecordingDisclosureBar implements } @UiThread - private void expand(String packageName) { - assertRevealingRecordingPackages(); - - final String label = getApplicationLabel(packageName); - mTextView.setText(mContext.getString(R.string.app_accessed_mic, label)); - - final AnimatorSet set = new AnimatorSet(); - set.playTogether( - ObjectAnimator.ofFloat(mIconTextsContainer, View.TRANSLATION_X, 0), - ObjectAnimator.ofFloat(mIconContainerBg, View.ALPHA, 1f), - ObjectAnimator.ofFloat(mTextsContainers, View.ALPHA, 1f), - ObjectAnimator.ofFloat(mBgEnd, View.ALPHA, 1f)); - set.setDuration(ANIMATION_DURATION); - set.addListener( - new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - onExpanded(); - } - }); - set.start(); - - mState = STATE_MAXIMIZING; - } - - @UiThread - private void minimize() { - assertRevealingRecordingPackages(); - - final int targetOffset = (mIsLtr ? 1 : -1) * mTextsContainers.getWidth(); - final AnimatorSet set = new AnimatorSet(); - set.playTogether( - ObjectAnimator.ofFloat(mIconTextsContainer, View.TRANSLATION_X, targetOffset), - ObjectAnimator.ofFloat(mIconContainerBg, View.ALPHA, 0f), - ObjectAnimator.ofFloat(mTextsContainers, View.ALPHA, 0f), - ObjectAnimator.ofFloat(mBgEnd, View.ALPHA, 0f)); - set.setDuration(ANIMATION_DURATION); - set.addListener( - new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - onMinimized(); - } - }); - set.start(); - - mState = STATE_MINIMIZING; - } - - @UiThread private void hide() { - if (DEBUG) { - Log.d(TAG, "Hide indicator"); - } + if (DEBUG) Log.d(TAG, "Hide indicator"); + final int targetOffset = (mIsLtr ? 1 : -1) * (mIndicatorView.getWidth() - (int) mIconTextsContainer.getTranslationX()); final AnimatorSet set = new AnimatorSet(); @@ -464,51 +324,37 @@ public class AudioRecordingDisclosureBar implements mState = STATE_DISAPPEARING; } - @UiThread - private void onExpanded() { - if (mState == STATE_STOPPED) { - return; - } - assertRevealingRecordingPackages(); + @UiThread + private void onAppeared() { + if (mState == STATE_STOPPED) return; mState = STATE_SHOWN; - mIndicatorView.postDelayed(this::minimize, MAXIMIZED_DURATION); - } - - @UiThread - private void onMinimized() { - if (mState == STATE_STOPPED) { - return; - } - - mState = STATE_MINIMIZED; - - if (mRevealRecordingPackages) { - if (!mPendingNotificationPackages.isEmpty()) { - // There is a new application that started recording, tell the user about it. - expand(mPendingNotificationPackages.poll()); - } else { - hideIndicatorIfNeeded(); - } - } + hideIndicatorIfNeeded(); } @UiThread private void onHidden() { - if (mState == STATE_STOPPED) { - return; - } + if (mState == STATE_STOPPED) return; removeIndicatorView(); mState = STATE_NOT_SHOWN; - // Check if anybody started recording while we were in STATE_DISAPPEARING - if (!mPendingNotificationPackages.isEmpty()) { - // There is a new application that started recording, tell the user about it. - show(mPendingNotificationPackages.poll()); + if (hasActiveRecorders()) { + // Got new recorders, show again. + showIfNotShown(); + } + } + + private boolean hasActiveRecorders() { + for (int index = mAudioActivityObservers.length - 1; index >= 0; index--) { + for (String activePackage : mAudioActivityObservers[index].getActivePackages()) { + if (mExemptPackages.contains(activePackage)) continue; + return true; + } } + return false; } private void removeIndicatorView() { @@ -525,57 +371,26 @@ public class AudioRecordingDisclosureBar implements mBgEnd = null; } - private String[] getGlobalStringArray(String setting) { - String result = Settings.Global.getString(mContext.getContentResolver(), setting); - return TextUtils.isEmpty(result) ? new String[0] : result.split(","); + private static List<String> splitByComma(String string) { + return TextUtils.isEmpty(string) ? Collections.emptyList() : Arrays.asList( + string.split(",")); } - private String getApplicationLabel(String packageName) { - assertRevealingRecordingPackages(); - - final PackageManager pm = mContext.getPackageManager(); - final ApplicationInfo appInfo; - try { - appInfo = pm.getApplicationInfo(packageName, 0); - } catch (PackageManager.NameNotFoundException e) { - return packageName; - } - return pm.getApplicationLabel(appInfo).toString(); - } - - private void assertRevealingRecordingPackages() { - if (!mRevealRecordingPackages) { - Log.e(TAG, "Not revealing recording packages", - DEBUG ? new RuntimeException("Should not be called") : null); - } - } - - private boolean checkIfEnabledInSettings() { - // 0 = disabled, everything else = enabled. Enabled by default. - return Settings.Global.getInt(mContext.getContentResolver(), - ENABLE_FLAG, 1) != 0; - } - - private void registerSettingsObserver() { - final ContentObserver contentObserver = new ContentObserver( - mContext.getMainThreadHandler()) { - @Override - public void onChange(boolean selfChange) { - if (mIsEnabledInSettings == checkIfEnabledInSettings()) { - // Nothing changed as we know it - ignore. - return; - } - - // Things changed: flip the flag. - mIsEnabledInSettings = !mIsEnabledInSettings; - if (mIsEnabledInSettings) { - start(); - } else { - stop(); + private final DeviceConfig.OnPropertiesChangedListener mConfigChangeListener = + new DeviceConfig.OnPropertiesChangedListener() { + @Override + public void onPropertiesChanged(DeviceConfig.Properties properties) { + reloadExemptPackages(); + + // Check if was enabled/disabled + if (mIsEnabled != properties.getBoolean(ENABLED_FLAG, true)) { + mIsEnabled = !mIsEnabled; + if (mIsEnabled) { + start(); + } else { + stop(); + } + } } - } - }; - mContext.getContentResolver().registerContentObserver( - Settings.Global.getUriFor(ENABLE_FLAG), false, contentObserver); - } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index f31f8eb0b20d..132e092b8ada 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -36,6 +36,7 @@ import android.util.Log; import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.google.android.collect.Sets; @@ -48,7 +49,6 @@ import java.util.Map; import java.util.Set; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controls the application of theme overlays across the system for all users. @@ -59,7 +59,7 @@ import javax.inject.Singleton; * - Observing work profile changes and applying overlays from the primary user to their * associated work profiles */ -@Singleton +@SysUISingleton public class ThemeOverlayController extends SystemUI { private static final String TAG = "ThemeOverlayController"; private static final boolean DEBUG = false; diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java index 9b465ae15478..a2203732c47c 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java @@ -33,17 +33,17 @@ import android.widget.ToastPresenter; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.CommandQueue; import java.util.Objects; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controls display of text toasts. */ -@Singleton +@SysUISingleton public class ToastUI extends SystemUI implements CommandQueue.Callbacks { private static final String TAG = "ToastUI"; diff --git a/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java b/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java index 25ae09840600..8a8f92b32bfe 100644 --- a/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java +++ b/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java @@ -25,6 +25,7 @@ import android.os.SystemClock; import androidx.annotation.NonNull; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.shared.tracing.FrameProtoTracer; import com.android.systemui.shared.tracing.FrameProtoTracer.ProtoTraceParams; @@ -42,12 +43,11 @@ import java.util.ArrayList; import java.util.Queue; import javax.inject.Inject; -import javax.inject.Singleton; /** * Controller for coordinating winscope proto tracing. */ -@Singleton +@SysUISingleton public class ProtoTracer implements Dumpable, ProtoTraceParams<MessageNano, SystemUiTraceFileProto, SystemUiTraceEntryProto, SystemUiTraceProto> { diff --git a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java index 49ada1a5e41e..1f444340653d 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java @@ -15,14 +15,10 @@ */ package com.android.systemui.tuner; -import android.content.ContentResolver; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; -import android.database.ContentObserver; import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.provider.Settings; import android.view.MenuItem; import androidx.preference.Preference; @@ -33,13 +29,13 @@ import androidx.preference.SwitchPreference; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.systemui.DemoMode; import com.android.systemui.R; +import com.android.systemui.demomode.DemoMode; +import com.android.systemui.demomode.DemoModeAvailabilityTracker; +import com.android.systemui.demomode.DemoModeController; public class DemoModeFragment extends PreferenceFragment implements OnPreferenceChangeListener { - private static final String DEMO_MODE_ON = "sysui_tuner_demo_on"; - private static final String[] STATUS_ICONS = { "volume", "bluetooth", @@ -57,6 +53,17 @@ public class DemoModeFragment extends PreferenceFragment implements OnPreference private SwitchPreference mEnabledSwitch; private SwitchPreference mOnSwitch; + private DemoModeController mDemoModeController; + private Tracker mDemoModeTracker; + + // We are the only ones who ever call this constructor, so don't worry about the warning + @SuppressLint("ValidFragment") + public DemoModeFragment(DemoModeController demoModeController) { + super(); + mDemoModeController = demoModeController; + } + + @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { Context context = getContext(); @@ -73,13 +80,11 @@ public class DemoModeFragment extends PreferenceFragment implements OnPreference screen.addPreference(mOnSwitch); setPreferenceScreen(screen); + mDemoModeTracker = new Tracker(context); + mDemoModeTracker.startTracking(); updateDemoModeEnabled(); updateDemoModeOn(); - ContentResolver contentResolver = getContext().getContentResolver(); - contentResolver.registerContentObserver(Settings.Global.getUriFor( - DemoMode.DEMO_MODE_ALLOWED), false, mDemoModeObserver); - contentResolver.registerContentObserver(Settings.Global.getUriFor(DEMO_MODE_ON), false, - mDemoModeObserver); + setHasOptionsMenu(true); } @@ -107,21 +112,17 @@ public class DemoModeFragment extends PreferenceFragment implements OnPreference @Override public void onDestroy() { - getContext().getContentResolver().unregisterContentObserver(mDemoModeObserver); + mDemoModeTracker.stopTracking(); super.onDestroy(); } private void updateDemoModeEnabled() { - boolean enabled = Settings.Global.getInt(getContext().getContentResolver(), - DemoMode.DEMO_MODE_ALLOWED, 0) != 0; - mEnabledSwitch.setChecked(enabled); - mOnSwitch.setEnabled(enabled); + mEnabledSwitch.setChecked(mDemoModeTracker.isDemoModeAvailable()); + mOnSwitch.setEnabled(mDemoModeTracker.isDemoModeAvailable()); } private void updateDemoModeOn() { - boolean enabled = Settings.Global.getInt(getContext().getContentResolver(), - DEMO_MODE_ON, 0) != 0; - mOnSwitch.setChecked(enabled); + mOnSwitch.setChecked(mDemoModeTracker.isInDemoMode()); } @Override @@ -134,7 +135,7 @@ public class DemoModeFragment extends PreferenceFragment implements OnPreference stopDemoMode(); } MetricsLogger.action(getContext(), MetricsEvent.TUNER_DEMO_MODE_ENABLED, enabled); - setGlobal(DemoMode.DEMO_MODE_ALLOWED, enabled ? 1 : 0); + mDemoModeController.requestSetDemoModeAllowed(enabled); } else if (preference == mOnSwitch) { MetricsLogger.action(getContext(), MetricsEvent.TUNER_DEMO_MODE_ON, enabled); if (enabled) { @@ -151,11 +152,11 @@ public class DemoModeFragment extends PreferenceFragment implements OnPreference private void startDemoMode() { Intent intent = new Intent(DemoMode.ACTION_DEMO); - intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_ENTER); - getContext().sendBroadcast(intent); + mDemoModeController.requestStartDemoMode(); intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_CLOCK); + //TODO: everything below should move to DemoModeController, or some `initialState` command String demoTime = "1010"; // 10:10, a classic choice of horologists try { String[] versionParts = android.os.Build.VERSION.RELEASE_OR_CODENAME.split("\\."); @@ -194,25 +195,31 @@ public class DemoModeFragment extends PreferenceFragment implements OnPreference intent.putExtra("visible", "false"); getContext().sendBroadcast(intent); - setGlobal(DEMO_MODE_ON, 1); } private void stopDemoMode() { - Intent intent = new Intent(DemoMode.ACTION_DEMO); - intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_EXIT); - getContext().sendBroadcast(intent); - setGlobal(DEMO_MODE_ON, 0); + mDemoModeController.requestFinishDemoMode(); } - private void setGlobal(String key, int value) { - Settings.Global.putInt(getContext().getContentResolver(), key, value); - } + private class Tracker extends DemoModeAvailabilityTracker { + Tracker(Context context) { + super(context); + } - private final ContentObserver mDemoModeObserver = - new ContentObserver(new Handler(Looper.getMainLooper())) { - public void onChange(boolean selfChange) { + @Override + public void onDemoModeAvailabilityChanged() { updateDemoModeEnabled(); updateDemoModeOn(); - }; + } + + @Override + public void onDemoModeStarted() { + updateDemoModeOn(); + } + + @Override + public void onDemoModeFinished() { + updateDemoModeOn(); + } }; } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java index fa531b5243b4..87d2063d18e9 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java @@ -14,18 +14,18 @@ package com.android.systemui.tuner; -import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY; -import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_CODE_END; -import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_CODE_START; -import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_IMAGE_DELIM; -import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.MENU_IME_ROTATE; -import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAVSPACE; -import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_LEFT; -import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_RIGHT; -import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_VIEWS; -import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.extractButton; -import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.extractImage; -import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.extractKeycode; +import static com.android.systemui.navigationbar.NavigationBarInflaterView.KEY; +import static com.android.systemui.navigationbar.NavigationBarInflaterView.KEY_CODE_END; +import static com.android.systemui.navigationbar.NavigationBarInflaterView.KEY_CODE_START; +import static com.android.systemui.navigationbar.NavigationBarInflaterView.KEY_IMAGE_DELIM; +import static com.android.systemui.navigationbar.NavigationBarInflaterView.MENU_IME_ROTATE; +import static com.android.systemui.navigationbar.NavigationBarInflaterView.NAVSPACE; +import static com.android.systemui.navigationbar.NavigationBarInflaterView.NAV_BAR_LEFT; +import static com.android.systemui.navigationbar.NavigationBarInflaterView.NAV_BAR_RIGHT; +import static com.android.systemui.navigationbar.NavigationBarInflaterView.NAV_BAR_VIEWS; +import static com.android.systemui.navigationbar.NavigationBarInflaterView.extractButton; +import static com.android.systemui.navigationbar.NavigationBarInflaterView.extractImage; +import static com.android.systemui.navigationbar.NavigationBarInflaterView.extractKeycode; import android.annotation.Nullable; import android.app.AlertDialog; @@ -51,6 +51,7 @@ import com.android.systemui.tuner.TunerService.Tunable; import java.util.ArrayList; +@Deprecated public class NavBarTuner extends TunerPreferenceFragment { private static final String LAYOUT = "layout"; diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java b/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java index 8f3a8f6ec960..d54c07c87b40 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java @@ -19,10 +19,10 @@ import android.view.View; import android.view.WindowManager; import com.android.systemui.Dependency; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.tuner.TunerService.Tunable; import javax.inject.Inject; -import javax.inject.Singleton; /** * Version of Space that can be resized by a tunable setting. @@ -77,7 +77,7 @@ public class TunablePadding implements Tunable { /** * Exists for easy injecting in tests. */ - @Singleton + @SysUISingleton public static class TunablePaddingService { private final TunerService mTunerService; diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java index 453c2f7da71f..78341edefbb2 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java @@ -31,6 +31,7 @@ import androidx.preference.PreferenceScreen; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.fragments.FragmentService; import javax.inject.Inject; @@ -41,9 +42,12 @@ public class TunerActivity extends Activity implements private static final String TAG_TUNER = "tuner"; + private final DemoModeController mDemoModeController; + @Inject - TunerActivity() { + TunerActivity(DemoModeController demoModeController) { super(); + mDemoModeController = demoModeController; } protected void onCreate(Bundle savedInstanceState) { @@ -61,7 +65,8 @@ public class TunerActivity extends Activity implements final String action = getIntent().getAction(); boolean showDemoMode = action != null && action.equals( "com.android.settings.action.DEMO_MODE"); - final PreferenceFragment fragment = showDemoMode ? new DemoModeFragment() + final PreferenceFragment fragment = showDemoMode + ? new DemoModeFragment(mDemoModeController) : new TunerFragment(); getFragmentManager().beginTransaction().replace(R.id.content_frame, fragment, TAG_TUNER).commit(); diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index 644f7582f146..d9727a73f651 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -18,7 +18,6 @@ package com.android.systemui.tuner; import android.app.ActivityManager; import android.content.ContentResolver; import android.content.Context; -import android.content.Intent; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.net.Uri; @@ -33,9 +32,10 @@ import android.util.ArraySet; import com.android.internal.util.ArrayUtils; import com.android.systemui.DejankUtils; -import com.android.systemui.DemoMode; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.qs.QSTileHost; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -46,14 +46,14 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.inject.Inject; -import javax.inject.Singleton; /** */ -@Singleton +@SysUISingleton public class TunerServiceImpl extends TunerService { + private static final String TAG = "TunerService"; private static final String TUNER_VERSION = "sysui_tuner_version"; private static final int CURRENT_TUNER_VERSION = 4; @@ -63,7 +63,8 @@ public class TunerServiceImpl extends TunerService { private static final String[] RESET_EXCEPTION_LIST = new String[] { QSTileHost.TILES_SETTING, Settings.Secure.DOZE_ALWAYS_ON, - Settings.Secure.MEDIA_CONTROLS_RESUME + Settings.Secure.MEDIA_CONTROLS_RESUME, + Secure.MEDIA_CONTROLS_RESUME_BLOCKED }; private final Observer mObserver = new Observer(); @@ -76,6 +77,7 @@ public class TunerServiceImpl extends TunerService { private final HashSet<Tunable> mTunables = LeakDetector.ENABLED ? new HashSet<>() : null; private final Context mContext; private final LeakDetector mLeakDetector; + private final DemoModeController mDemoModeController; private ContentResolver mContentResolver; private int mCurrentUser; @@ -84,11 +86,16 @@ public class TunerServiceImpl extends TunerService { /** */ @Inject - public TunerServiceImpl(Context context, @Main Handler mainHandler, - LeakDetector leakDetector, BroadcastDispatcher broadcastDispatcher) { + public TunerServiceImpl( + Context context, + @Main Handler mainHandler, + LeakDetector leakDetector, + DemoModeController demoModeController, + BroadcastDispatcher broadcastDispatcher) { mContext = context; mContentResolver = mContext.getContentResolver(); mLeakDetector = leakDetector; + mDemoModeController = demoModeController; for (UserInfo user : UserManager.get(mContext).getUsers()) { mCurrentUser = user.getUserHandle().getIdentifier(); @@ -244,12 +251,11 @@ public class TunerServiceImpl extends TunerService { } public void clearAllFromUser(int user) { - // A couple special cases. - Settings.Global.putString(mContentResolver, DemoMode.DEMO_MODE_ALLOWED, null); - Intent intent = new Intent(DemoMode.ACTION_DEMO); - intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_EXIT); - mContext.sendBroadcast(intent); + // Turn off demo mode + mDemoModeController.requestFinishDemoMode(); + mDemoModeController.requestSetDemoModeAllowed(false); + // A couple special cases. for (String key : mTunableLookup.keySet()) { if (ArrayUtils.contains(RESET_EXCEPTION_LIST, key)) { continue; diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java new file mode 100644 index 000000000000..37aac1124048 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 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 com.android.systemui.tv; + +import com.android.systemui.dagger.GlobalRootComponent; + +import javax.inject.Singleton; + +import dagger.Component; + +/** + * Root component for Dagger injection. + */ +@Singleton +@Component(modules = {TvSysUIComponentModule.class}) +public interface TvGlobalRootComponent extends GlobalRootComponent { + /** + * Component Builder interface. This allows to bind Context instance in the component + */ + @Component.Builder + interface Builder extends GlobalRootComponent.Builder { + TvGlobalRootComponent build(); + } + + @Override + TvSysUIComponent.Builder getSysUIComponent(); +} diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java index dce38c109930..b7bc8c8fb7c4 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java @@ -19,22 +19,20 @@ package com.android.systemui.tv; import com.android.systemui.dagger.DefaultComponentBinder; import com.android.systemui.dagger.DependencyBinder; import com.android.systemui.dagger.DependencyProvider; +import com.android.systemui.dagger.SysUIComponent; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SystemServicesModule; import com.android.systemui.dagger.SystemUIBinder; -import com.android.systemui.dagger.SystemUIDefaultModule; import com.android.systemui.dagger.SystemUIModule; -import com.android.systemui.dagger.SystemUIRootComponent; import com.android.systemui.onehanded.dagger.OneHandedModule; -import javax.inject.Singleton; - -import dagger.Component; +import dagger.Subcomponent; /** - * Root component for Dagger injection. + * Dagger Subcomponent for Core SysUI. */ -@Singleton -@Component(modules = { +@SysUISingleton +@Subcomponent(modules = { DefaultComponentBinder.class, DependencyProvider.class, DependencyBinder.class, @@ -42,14 +40,15 @@ import dagger.Component; SystemServicesModule.class, SystemUIBinder.class, SystemUIModule.class, - SystemUIDefaultModule.class, + TvSystemUIModule.class, TvSystemUIBinder.class}) -public interface TvSystemUIRootComponent extends SystemUIRootComponent { +public interface TvSysUIComponent extends SysUIComponent { + /** - * Component Builder interface. This allows to bind Context instance in the component + * Builder for a SysUIComponent. */ - @Component.Builder - interface Builder extends SystemUIRootComponent.Builder { - TvSystemUIRootComponent build(); + @Subcomponent.Builder + interface Builder extends SysUIComponent.Builder { + TvSysUIComponent build(); } } diff --git a/tests/AutoVerify/app3/src/com/android/test/autoverify/MainActivity.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponentModule.java index 09ef47212622..334bb013ae69 100644 --- a/tests/AutoVerify/app3/src/com/android/test/autoverify/MainActivity.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponentModule.java @@ -13,3 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +package com.android.systemui.tv; + +import dagger.Module; + +/** + * Dagger module for including the WMComponent. + */ +@Module(subcomponents = {TvSysUIComponent.class}) +public abstract class TvSysUIComponentModule { +} diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java index be30a4a4c72e..9a44bf12a3ef 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java @@ -16,7 +16,7 @@ package com.android.systemui.tv; -import com.android.systemui.dagger.SystemUIRootComponent; +import com.android.systemui.dagger.GlobalRootComponent; import com.android.systemui.pip.tv.dagger.PipModule; import dagger.Binds; @@ -25,5 +25,5 @@ import dagger.Module; @Module(includes = {PipModule.class}) interface TvSystemUIBinder { @Binds - SystemUIRootComponent bindSystemUIRootComponent(TvSystemUIRootComponent systemUIRootComponent); + GlobalRootComponent bindGlobalRootComponent(TvGlobalRootComponent globalRootComponent); } diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java index 7d3ec678fd5f..c99ad23ab23d 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java @@ -19,16 +19,16 @@ package com.android.systemui.tv; import android.content.Context; import com.android.systemui.SystemUIFactory; -import com.android.systemui.dagger.SystemUIRootComponent; +import com.android.systemui.dagger.GlobalRootComponent; /** - * TV variant {@link SystemUIFactory}, that substitutes default {@link SystemUIRootComponent} for - * {@link TvSystemUIRootComponent} + * TV variant {@link SystemUIFactory}, that substitutes default {@link GlobalRootComponent} for + * {@link TvGlobalRootComponent} */ public class TvSystemUIFactory extends SystemUIFactory { @Override - protected SystemUIRootComponent buildSystemUIRootComponent(Context context) { - return DaggerTvSystemUIRootComponent.builder() + protected GlobalRootComponent buildGlobalRootComponent(Context context) { + return DaggerTvGlobalRootComponent.builder() .context(context) .build(); } diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java new file mode 100644 index 000000000000..ca9cb08c0a59 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2019 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 com.android.systemui.tv; + +import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; +import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME; + +import android.content.Context; +import android.os.Handler; +import android.os.PowerManager; + +import androidx.annotation.Nullable; + +import com.android.keyguard.KeyguardViewController; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.demomode.DemoModeController; +import com.android.systemui.dock.DockManager; +import com.android.systemui.dock.DockManagerImpl; +import com.android.systemui.doze.DozeHost; +import com.android.systemui.plugins.qs.QSFactory; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.power.EnhancedEstimates; +import com.android.systemui.power.EnhancedEstimatesImpl; +import com.android.systemui.qs.dagger.QSModule; +import com.android.systemui.qs.tileimpl.QSFactoryImpl; +import com.android.systemui.recents.Recents; +import com.android.systemui.recents.RecentsImplementation; +import com.android.systemui.stackdivider.DividerModule; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; +import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.phone.DozeServiceHost; +import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; +import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl; +import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl; +import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.ShadeControllerImpl; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.BatteryControllerImpl; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; +import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.wmshell.WMShellModule; + +import javax.inject.Named; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; + +/** + * A dagger module for injecting default implementations of components of System UI that may be + * overridden by the System UI implementation. + */ +@Module(includes = { + DividerModule.class, + QSModule.class, + WMShellModule.class + }, + subcomponents = { + }) +public abstract class TvSystemUIModule { + + @SysUISingleton + @Provides + @Named(LEAK_REPORT_EMAIL_NAME) + @Nullable + static String provideLeakReportEmail() { + return null; + } + + @Binds + abstract EnhancedEstimates bindEnhancedEstimates(EnhancedEstimatesImpl enhancedEstimates); + + @Binds + abstract NotificationLockscreenUserManager bindNotificationLockscreenUserManager( + NotificationLockscreenUserManagerImpl notificationLockscreenUserManager); + + @Provides + @SysUISingleton + static BatteryController provideBatteryController(Context context, + EnhancedEstimates enhancedEstimates, PowerManager powerManager, + BroadcastDispatcher broadcastDispatcher, DemoModeController demoModeController, + @Main Handler mainHandler, @Background Handler bgHandler) { + BatteryController bC = new BatteryControllerImpl(context, enhancedEstimates, powerManager, + broadcastDispatcher, demoModeController, mainHandler, bgHandler); + bC.init(); + return bC; + } + + @Binds + @SysUISingleton + abstract QSFactory bindQSFactory(QSFactoryImpl qsFactoryImpl); + + @Binds + abstract DockManager bindDockManager(DockManagerImpl dockManager); + + @Binds + abstract NotificationEntryManager.KeyguardEnvironment bindKeyguardEnvironment( + KeyguardEnvironmentImpl keyguardEnvironment); + + @Binds + abstract ShadeController provideShadeController(ShadeControllerImpl shadeController); + + @SysUISingleton + @Provides + @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) + static boolean provideAllowNotificationLongPress() { + return true; + } + + @SysUISingleton + @Provides + static HeadsUpManagerPhone provideHeadsUpManagerPhone( + Context context, + StatusBarStateController statusBarStateController, + KeyguardBypassController bypassController, + NotificationGroupManager groupManager, + ConfigurationController configurationController) { + return new HeadsUpManagerPhone(context, statusBarStateController, bypassController, + groupManager, configurationController); + } + + @Binds + abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone); + + @Provides + @SysUISingleton + static Recents provideRecents(Context context, RecentsImplementation recentsImplementation, + CommandQueue commandQueue) { + return new Recents(context, recentsImplementation, commandQueue); + } + + @Binds + abstract DeviceProvisionedController bindDeviceProvisionedController( + DeviceProvisionedControllerImpl deviceProvisionedController); + + @Binds + abstract KeyguardViewController bindKeyguardViewController( + StatusBarKeyguardViewManager statusBarKeyguardViewManager); + + @Binds + abstract NotificationShadeWindowController bindNotificationShadeController( + NotificationShadeWindowControllerImpl notificationShadeWindowController); + + @Binds + abstract DozeHost provideDozeHost(DozeServiceHost dozeServiceHost); +} diff --git a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt index 242f7cde9d3b..f22f59bee42f 100644 --- a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt @@ -2,10 +2,10 @@ package com.android.systemui.util import android.graphics.Rect import android.util.Log +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.util.FloatingContentCoordinator.FloatingContent -import java.util.HashMap +import java.util.* import javax.inject.Inject -import javax.inject.Singleton /** Tag for debug logging. */ private const val TAG = "FloatingCoordinator" @@ -20,7 +20,7 @@ private const val TAG = "FloatingCoordinator" * other content out of the way. [onContentRemoved] should be called when the content is removed or * no longer visible. */ -@Singleton +@SysUISingleton class FloatingContentCoordinator @Inject constructor() { /** diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java index d16bedcad960..d278905abacb 100644 --- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java +++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java @@ -23,16 +23,14 @@ import android.view.InflateException; import android.view.LayoutInflater; import android.view.View; -import com.android.keyguard.KeyguardClockSwitch; import com.android.keyguard.KeyguardMessageArea; import com.android.keyguard.KeyguardSliceView; -import com.android.systemui.dagger.SystemUIRootComponent; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.qs.QSFooterImpl; import com.android.systemui.qs.QSPanel; import com.android.systemui.qs.QuickQSPanel; import com.android.systemui.qs.QuickStatusBarHeader; import com.android.systemui.qs.customize.QSCustomizer; -import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import java.lang.reflect.InvocationTargetException; @@ -41,38 +39,28 @@ import java.lang.reflect.Modifier; import javax.inject.Inject; import javax.inject.Named; -import javax.inject.Singleton; -import dagger.Module; -import dagger.Provides; +import dagger.BindsInstance; import dagger.Subcomponent; /** * Manages inflation that requires dagger injection. * See docs/dagger.md for details. */ -@Singleton +@SysUISingleton public class InjectionInflationController { public static final String VIEW_CONTEXT = "view_context"; - private final ViewCreator mViewCreator; private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>(); private final LayoutInflater.Factory2 mFactory = new InjectionFactory(); + private final ViewInstanceCreator.Factory mViewInstanceCreatorFactory; @Inject - public InjectionInflationController(SystemUIRootComponent rootComponent) { - mViewCreator = rootComponent.createViewCreator(); + public InjectionInflationController(ViewInstanceCreator.Factory viewInstanceCreatorFactory) { + mViewInstanceCreatorFactory = viewInstanceCreatorFactory; initInjectionMap(); } - ArrayMap<String, Method> getInjectionMap() { - return mInjectionMap; - } - - ViewCreator getFragmentCreator() { - return mViewCreator; - } - /** * Wraps a {@link LayoutInflater} to support creating dagger injected views. * See docs/dagger.md for details. @@ -93,25 +81,19 @@ public class InjectionInflationController { } /** - * The subcomponent of dagger that holds all views that need injection. + * Subcomponent that actually creates injected views. */ @Subcomponent - public interface ViewCreator { - /** - * Creates another subcomponent to actually generate the view. - */ - ViewInstanceCreator createInstanceCreator(ViewAttributeProvider attributeProvider); - } - - /** - * Secondary sub-component that actually creates the views. - * - * Having two subcomponents lets us hide the complexity of providing the named context - * and AttributeSet from the SystemUIRootComponent, instead we have one subcomponent that - * creates a new ViewInstanceCreator any time we need to inflate a view. - */ - @Subcomponent(modules = ViewAttributeProvider.class) public interface ViewInstanceCreator { + + /** Factory for creating a ViewInstanceCreator. */ + @Subcomponent.Factory + interface Factory { + ViewInstanceCreator build( + @BindsInstance @Named(VIEW_CONTEXT) Context context, + @BindsInstance AttributeSet attributeSet); + } + /** * Creates the QuickStatusBarHeader. */ @@ -152,36 +134,6 @@ public class InjectionInflationController { QSCustomizer createQSCustomizer(); } - /** - * Module for providing view-specific constructor objects. - */ - @Module - public class ViewAttributeProvider { - private final Context mContext; - private final AttributeSet mAttrs; - - private ViewAttributeProvider(Context context, AttributeSet attrs) { - mContext = context; - mAttrs = attrs; - } - - /** - * Provides the view-themed context (as opposed to the global sysui application context). - */ - @Provides - @Named(VIEW_CONTEXT) - public Context provideContext() { - return mContext; - } - - /** - * Provides the AttributeSet for the current view being inflated. - */ - @Provides - public AttributeSet provideAttributeSet() { - return mAttrs; - } - } private class InjectionFactory implements LayoutInflater.Factory2 { @@ -189,10 +141,9 @@ public class InjectionInflationController { public View onCreateView(String name, Context context, AttributeSet attrs) { Method creationMethod = mInjectionMap.get(name); if (creationMethod != null) { - ViewAttributeProvider provider = new ViewAttributeProvider(context, attrs); try { return (View) creationMethod.invoke( - mViewCreator.createInstanceCreator(provider)); + mViewInstanceCreatorFactory.build(context, attrs)); } catch (IllegalAccessException e) { throw new InflateException("Could not inflate " + name, e); } catch (InvocationTargetException e) { diff --git a/packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt index 58684c386995..751324102036 100644 --- a/packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt @@ -25,12 +25,12 @@ import android.os.UserHandle import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import java.util.concurrent.Executor import javax.inject.Inject -import javax.inject.Singleton -@Singleton +@SysUISingleton class RingerModeTrackerImpl @Inject constructor( audioManager: AudioManager, broadcastDispatcher: BroadcastDispatcher, diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java index e5f30cf63ac3..72f1f22c0ba1 100644 --- a/packages/SystemUI/src/com/android/systemui/util/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java @@ -21,12 +21,15 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.provider.Settings; +import android.text.TextUtils; import android.view.View; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.function.Consumer; public class Utils { @@ -143,4 +146,21 @@ public class Utils { Settings.Secure.MEDIA_CONTROLS_RESUME, 1); return useQsMediaPlayer(context) && flag > 0; } + + /** + * Get the set of apps for which the user has manually disabled resumption. + */ + public static Set<String> getBlockedMediaApps(Context context) { + String list = Settings.Secure.getString(context.getContentResolver(), + Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED); + if (TextUtils.isEmpty(list)) { + return new HashSet<>(); + } + String[] names = list.split(":"); + Set<String> apps = new HashSet<>(names.length); + for (String s : names) { + apps.add(s); + } + return apps; + } } diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java index bf22a9897d16..628c808aa12b 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java @@ -22,6 +22,7 @@ import android.os.HandlerThread; import android.os.Looper; import android.os.Process; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.LongRunning; import com.android.systemui.dagger.qualifiers.Main; @@ -30,8 +31,6 @@ import com.android.systemui.dagger.qualifiers.UiBackground; import java.util.concurrent.Executor; import java.util.concurrent.Executors; -import javax.inject.Singleton; - import dagger.Binds; import dagger.Module; import dagger.Provides; @@ -43,7 +42,7 @@ import dagger.Provides; public abstract class ConcurrencyModule { /** Background Looper */ @Provides - @Singleton + @SysUISingleton @Background public static Looper provideBgLooper() { HandlerThread thread = new HandlerThread("SysUiBg", @@ -54,7 +53,7 @@ public abstract class ConcurrencyModule { /** Long running tasks Looper */ @Provides - @Singleton + @SysUISingleton @LongRunning public static Looper provideLongRunningLooper() { HandlerThread thread = new HandlerThread("SysUiLng", @@ -96,7 +95,7 @@ public abstract class ConcurrencyModule { * Provide a Background-Thread Executor by default. */ @Provides - @Singleton + @SysUISingleton public static Executor provideExecutor(@Background Looper looper) { return new ExecutorImpl(looper); } @@ -105,7 +104,7 @@ public abstract class ConcurrencyModule { * Provide a Long running Executor by default. */ @Provides - @Singleton + @SysUISingleton @LongRunning public static Executor provideLongRunningExecutor(@LongRunning Looper looper) { return new ExecutorImpl(looper); @@ -115,7 +114,7 @@ public abstract class ConcurrencyModule { * Provide a Background-Thread Executor. */ @Provides - @Singleton + @SysUISingleton @Background public static Executor provideBackgroundExecutor(@Background Looper looper) { return new ExecutorImpl(looper); @@ -134,7 +133,7 @@ public abstract class ConcurrencyModule { * Provide a Background-Thread Executor by default. */ @Provides - @Singleton + @SysUISingleton public static DelayableExecutor provideDelayableExecutor(@Background Looper looper) { return new ExecutorImpl(looper); } @@ -143,7 +142,7 @@ public abstract class ConcurrencyModule { * Provide a Background-Thread Executor. */ @Provides - @Singleton + @SysUISingleton @Background public static DelayableExecutor provideBackgroundDelayableExecutor(@Background Looper looper) { return new ExecutorImpl(looper); @@ -153,7 +152,7 @@ public abstract class ConcurrencyModule { * Provide a Main-Thread Executor. */ @Provides - @Singleton + @SysUISingleton @Main public static DelayableExecutor provideMainDelayableExecutor(@Main Looper looper) { return new ExecutorImpl(looper); @@ -163,7 +162,7 @@ public abstract class ConcurrencyModule { * Provide a Background-Thread Executor by default. */ @Provides - @Singleton + @SysUISingleton public static RepeatableExecutor provideRepeatableExecutor(@Background DelayableExecutor exec) { return new RepeatableExecutorImpl(exec); } @@ -172,7 +171,7 @@ public abstract class ConcurrencyModule { * Provide a Background-Thread Executor. */ @Provides - @Singleton + @SysUISingleton @Background public static RepeatableExecutor provideBackgroundRepeatableExecutor( @Background DelayableExecutor exec) { @@ -183,7 +182,7 @@ public abstract class ConcurrencyModule { * Provide a Main-Thread Executor. */ @Provides - @Singleton + @SysUISingleton @Main public static RepeatableExecutor provideMainRepeatableExecutor(@Main DelayableExecutor exec) { return new RepeatableExecutorImpl(exec); @@ -195,7 +194,7 @@ public abstract class ConcurrencyModule { * Keep submitted runnables short and to the point, just as with any other UI code. */ @Provides - @Singleton + @SysUISingleton @UiBackground public static Executor provideUiBackgroundExecutor() { return Executors.newSingleThreadExecutor(); diff --git a/packages/SystemUI/src/com/android/systemui/util/io/Files.java b/packages/SystemUI/src/com/android/systemui/util/io/Files.java index 7d633a769600..a2d309cf76d4 100644 --- a/packages/SystemUI/src/com/android/systemui/util/io/Files.java +++ b/packages/SystemUI/src/com/android/systemui/util/io/Files.java @@ -18,6 +18,8 @@ package com.android.systemui.util.io; import androidx.annotation.NonNull; +import com.android.systemui.dagger.SysUISingleton; + import java.io.BufferedWriter; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -28,12 +30,11 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.stream.Stream; import javax.inject.Inject; -import javax.inject.Singleton; /** * Wrapper around {@link java.nio.file.Files} that can be mocked in tests. */ -@Singleton +@SysUISingleton public class Files { @Inject public Files() { } diff --git a/tests/AutoVerify/app1/src/com/android/test/autoverify/MainActivity.java b/packages/SystemUI/src/com/android/systemui/util/kotlin/nullability.kt index 09ef47212622..92c73a412577 100644 --- a/tests/AutoVerify/app1/src/com/android/test/autoverify/MainActivity.java +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/nullability.kt @@ -13,3 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +package com.android.systemui.util.kotlin + +/** + * If [value] is not null, then returns block(value). Otherwise returns null. + */ +inline fun <T : Any, R> transform(value: T?, block: (T) -> R): R? = value?.let(block) diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java index d1805af06434..ba58ed282786 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java @@ -48,6 +48,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; @@ -63,14 +64,13 @@ import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * Suite of tools to periodically inspect the System UI heap and possibly prompt the user to * capture heap dumps and report them. Includes the implementation of the "Dump SysUI Heap" * quick settings tile. */ -@Singleton +@SysUISingleton public class GarbageMonitor implements Dumpable { // Feature switches // ================ @@ -552,7 +552,7 @@ public class GarbageMonitor implements Dumpable { } /** */ - @Singleton + @SysUISingleton public static class Service extends SystemUI implements Dumpable { private final GarbageMonitor mGarbageMonitor; diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java b/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java index 5e7280840bb9..f0a4195beebe 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java @@ -34,6 +34,8 @@ import android.util.Log; import androidx.core.content.FileProvider; +import com.android.systemui.dagger.SysUISingleton; + import com.google.android.collect.Lists; import java.io.File; @@ -44,12 +46,11 @@ import java.util.ArrayList; import javax.inject.Inject; import javax.inject.Named; -import javax.inject.Singleton; /** * Dumps data to debug leaks and posts a notification to share the data. */ -@Singleton +@SysUISingleton public class LeakReporter { static final String TAG = "LeakReporter"; diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java index ed4df175b1a6..48759824f5ef 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java @@ -29,6 +29,7 @@ import android.os.MemoryFile; import android.util.Log; import com.android.internal.util.Preconditions; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.SensorManagerPlugin; import com.android.systemui.shared.plugins.PluginManager; @@ -39,7 +40,6 @@ import java.util.List; import java.util.concurrent.Executor; import javax.inject.Inject; -import javax.inject.Singleton; /** * Wrapper around sensor manager that hides potential sources of latency. @@ -48,7 +48,7 @@ import javax.inject.Singleton; * without blocking. Note that this means registering listeners now always appears successful even * if it is not. */ -@Singleton +@SysUISingleton public class AsyncSensorManager extends SensorManager implements PluginListener<SensorManagerPlugin> { diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java index 42393375b45e..e8c5db74f494 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java @@ -18,7 +18,7 @@ package com.android.systemui.volume; import android.content.res.Configuration; -import com.android.systemui.DemoMode; +import com.android.systemui.demomode.DemoMode; import java.io.FileDescriptor; import java.io.PrintWriter; diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java index 1c2a2fa53bea..56f1c092efd9 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java @@ -27,6 +27,9 @@ import android.view.WindowManager.LayoutParams; import com.android.settingslib.applications.InterestingConfigChanges; import com.android.systemui.Dependency; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.demomode.DemoMode; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.PluginDependencyProvider; @@ -38,14 +41,15 @@ import com.android.systemui.tuner.TunerService; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; /** * Implementation of VolumeComponent backed by the new volume dialog. */ -@Singleton +@SysUISingleton public class VolumeDialogComponent implements VolumeComponent, TunerService.Tunable, VolumeDialogControllerImpl.UserActivityListener{ @@ -72,8 +76,11 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna ); @Inject - public VolumeDialogComponent(Context context, KeyguardViewMediator keyguardViewMediator, - VolumeDialogControllerImpl volumeDialogController) { + public VolumeDialogComponent( + Context context, + KeyguardViewMediator keyguardViewMediator, + VolumeDialogControllerImpl volumeDialogController, + DemoModeController demoModeController) { mContext = context; mKeyguardViewMediator = keyguardViewMediator; mController = volumeDialogController; @@ -94,6 +101,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna applyConfiguration(); Dependency.get(TunerService.class).addTunable(this, VOLUME_DOWN_SILENT, VOLUME_UP_SILENT, VOLUME_SILENT_DO_NOT_DISTURB); + demoModeController.addCallback(this); } protected VolumeDialog createDefault() { @@ -164,6 +172,13 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna } @Override + public List<String> demoCommands() { + List<String> s = new ArrayList<>(); + s.add(DemoMode.COMMAND_VOLUME); + return s; + } + + @Override public void register() { mController.register(); DndTile.setCombinedIcon(mContext, true); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index f19c49cc123f..d8eecef024ce 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -62,6 +62,7 @@ import com.android.settingslib.volume.MediaSessions; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.qs.tiles.DndTile; @@ -77,7 +78,6 @@ import java.util.Objects; import java.util.Optional; import javax.inject.Inject; -import javax.inject.Singleton; import dagger.Lazy; @@ -88,7 +88,7 @@ import dagger.Lazy; * * Methods ending in "W" must be called on the worker thread. */ -@Singleton +@SysUISingleton public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable { private static final String TAG = Util.logTag(VolumeDialogControllerImpl.class); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 4b119dd7e176..51ad30ebcac6 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -940,6 +940,7 @@ public class VolumeDialogImpl implements VolumeDialog, protected void onStateChangedH(State state) { if (D.BUG) Log.d(TAG, "onStateChangedH() state: " + state.toString()); if (mState != null && state != null + && mState.ringerModeInternal != -1 && mState.ringerModeInternal != state.ringerModeInternal && state.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) { mController.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK)); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java index c0b80417fe14..c378e3bd76c9 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java @@ -23,15 +23,15 @@ import android.util.Log; import com.android.systemui.R; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.qs.tiles.DndTile; import java.io.FileDescriptor; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Singleton; -@Singleton +@SysUISingleton public class VolumeUI extends SystemUI { private static final String TAG = "VolumeUI"; private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java new file mode 100644 index 000000000000..ac47660f3221 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.wmshell; + +import android.content.Context; +import android.os.Handler; +import android.view.IWindowManager; + +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.SystemWindows; +import com.android.wm.shell.common.TransactionPool; + +import dagger.Module; +import dagger.Provides; + +/** + * Provides basic dependencies from {@link com.android.wm.shell}, the dependencies declared here + * should be shared among different branches of SystemUI. + */ +// TODO(b/162923491): Move most of these dependencies into WMSingleton scope. +@Module +public class WMShellBaseModule { + @SysUISingleton + @Provides + static TransactionPool provideTransactionPool() { + return new TransactionPool(); + } + + @SysUISingleton + @Provides + static DisplayController provideDisplayController(Context context, @Main Handler handler, + IWindowManager wmService) { + return new DisplayController(context, handler, wmService); + } + + @SysUISingleton + @Provides + static SystemWindows provideSystemWindows(DisplayController displayController, + IWindowManager wmService) { + return new SystemWindows(displayController, wmService); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index d2c61cc996dd..44bb6ac5a49c 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -16,50 +16,28 @@ package com.android.systemui.wmshell; -import android.content.Context; import android.os.Handler; import android.view.IWindowManager; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.pip.phone.PipMenuActivity; import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TransactionPool; -import javax.inject.Singleton; - import dagger.Module; import dagger.Provides; /** - * Provides dependencies from {@link com.android.wm.shell}. + * Provides dependencies from {@link com.android.wm.shell} which could be customized among different + * branches of SystemUI. */ -@Module -// TODO(b/161116823) Clean up dependencies after wm shell migration finished. -public class WindowManagerShellModule { - @Singleton - @Provides - static TransactionPool provideTransactionPool() { - return new TransactionPool(); - } - - @Singleton - @Provides - static DisplayController provideDisplayController(Context context, @Main Handler handler, - IWindowManager wmService) { - return new DisplayController(context, handler, wmService); - } - - @Singleton - @Provides - static SystemWindows provideSystemWindows(DisplayController displayController, - IWindowManager wmService) { - return new SystemWindows(displayController, wmService); - } - - @Singleton +// TODO(b/162923491): Move most of these dependencies into WMSingleton scope. +@Module(includes = WMShellBaseModule.class) +public class WMShellModule { + @SysUISingleton @Provides static DisplayImeController provideDisplayImeController(IWindowManager wmService, DisplayController displayController, @Main Handler mainHandler, @@ -69,7 +47,7 @@ public class WindowManagerShellModule { } /** TODO(b/150319024): PipMenuActivity will move to a Window */ - @Singleton + @SysUISingleton @PipMenuActivityClass @Provides static Class<?> providePipMenuActivityClass() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index 4c0762e4ea32..5999e2cdec78 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -79,7 +79,9 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { .thenReturn(mMockKeyguardSliceView); InjectionInflationController inflationController = new InjectionInflationController( - SystemUIFactory.getInstance().getRootComponent()); + SystemUIFactory.getInstance() + .getSysUIComponent() + .createViewInstanceCreatorFactory()); LayoutInflater layoutInflater = inflationController .injectable(LayoutInflater.from(getContext())); layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java index e6c2ddcf7e65..446b1228f1bb 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java @@ -65,7 +65,9 @@ public class KeyguardPresentationTest extends SysuiTestCase { allowTestableLooperAsMainThread(); InjectionInflationController inflationController = new InjectionInflationController( - SystemUIFactory.getInstance().getRootComponent()); + SystemUIFactory.getInstance() + .getSysUIComponent() + .createViewInstanceCreatorFactory()); mLayoutInflater = inflationController.injectable(LayoutInflater.from(mContext)); mLayoutInflater.setPrivateFactory(new LayoutInflater.Factory2() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java index bc3c3d995ce6..0bf137689aa1 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java @@ -51,7 +51,9 @@ public class KeyguardStatusViewTest extends SysuiTestCase { allowTestableLooperAsMainThread(); mDependency.injectMockDependency(KeyguardUpdateMonitor.class); InjectionInflationController inflationController = new InjectionInflationController( - SystemUIFactory.getInstance().getRootComponent()); + SystemUIFactory.getInstance() + .getSysUIComponent() + .createViewInstanceCreatorFactory()); LayoutInflater layoutInflater = inflationController .injectable(LayoutInflater.from(getContext())); mKeyguardStatusView = diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index a0e5f73b6a4c..11920233e403 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -182,7 +182,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // IBiometricsFace@1.0 does not support detection, only authentication. when(mFaceSensorProperties.isEmpty()).thenReturn(false); when(mFaceSensorProperties.get(anyInt())).thenReturn(new FaceSensorProperties(0 /* id */, - false /* supportsFaceDetection */)); + false /* supportsFaceDetection */, true /* supportsSelfIllumination */)); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java index 35be496d1a2c..5d8e4351cfd9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java @@ -47,7 +47,7 @@ public class DependencyTest extends SysuiTestCase { public void testInitDependency() { Dependency.clearDependencies(); Dependency dependency = - SystemUIFactory.getInstance().getRootComponent().createDependency(); + SystemUIFactory.getInstance().getSysUIComponent().createDependency(); dependency.start(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java index 60f0cd9da5f2..e967a5d607eb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java @@ -82,8 +82,7 @@ public class ForegroundServiceControllerTest extends SysuiTestCase { allowTestableLooperAsMainThread(); MockitoAnnotations.initMocks(this); - mFsc = new ForegroundServiceController( - mEntryManager, mAppOpsController, mMainHandler); + mFsc = new ForegroundServiceController(mAppOpsController, mMainHandler); mListener = new ForegroundServiceNotificationListener( mContext, mFsc, mEntryManager, mNotifPipeline, mock(ForegroundServiceLifetimeExtender.class), mClock); @@ -115,85 +114,6 @@ public class ForegroundServiceControllerTest extends SysuiTestCase { } @Test - public void testAppOps_appOpChangedBeforeNotificationExists() { - // GIVEN app op exists, but notification doesn't exist in NEM yet - NotificationEntry entry = createFgEntry(); - mFsc.onAppOpChanged( - AppOpsManager.OP_CAMERA, - entry.getSbn().getUid(), - entry.getSbn().getPackageName(), - true); - assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); - - // WHEN the notification is added - mEntryListener.onPendingEntryAdded(entry); - - // THEN the app op is added to the entry - Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); - } - - @Test - public void testAppOps_appOpAddedToForegroundNotif() { - // GIVEN a notification associated with a foreground service - NotificationEntry entry = addFgEntry(); - when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry); - - // WHEN we are notified of a new app op for this notification - mFsc.onAppOpChanged( - AppOpsManager.OP_CAMERA, - entry.getSbn().getUid(), - entry.getSbn().getPackageName(), - true); - - // THEN the app op is added to the entry - Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); - - // THEN notification views are updated since the notification is visible - verify(mEntryManager, times(1)).updateNotifications(anyString()); - } - - @Test - public void testAppOpsAlreadyAdded() { - // GIVEN a foreground service associated notification that already has the correct app op - NotificationEntry entry = addFgEntry(); - entry.mActiveAppOps.add(AppOpsManager.OP_CAMERA); - when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry); - - // WHEN we are notified of the same app op for this notification - mFsc.onAppOpChanged( - AppOpsManager.OP_CAMERA, - entry.getSbn().getUid(), - entry.getSbn().getPackageName(), - true); - - // THEN the app op still exists in the notification entry - Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); - - // THEN notification views aren't updated since nothing changed - verify(mEntryManager, never()).updateNotifications(anyString()); - } - - @Test - public void testAppOps_appOpNotAddedToUnrelatedNotif() { - // GIVEN no notification entries correspond to the newly updated appOp - NotificationEntry entry = addFgEntry(); - when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(null); - - // WHEN a new app op is detected - mFsc.onAppOpChanged( - AppOpsManager.OP_CAMERA, - entry.getSbn().getUid(), - entry.getSbn().getPackageName(), - true); - - // THEN we won't see appOps on the entry - Assert.assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA)); - - // THEN notification views aren't updated since nothing changed - verify(mEntryManager, never()).updateNotifications(anyString()); - } - - @Test public void testAppOpsCRUD() { // no crash on remove that doesn't exist mFsc.onAppOpChanged(9, 1000, "pkg1", false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java index 3687b4ca7889..53bae8657d8f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java @@ -55,7 +55,7 @@ public abstract class SysuiBaseFragmentTest extends BaseFragmentTest { public void SysuiSetup() { SystemUIFactory.createFromConfig(mContext); mDependency = new TestableDependency( - SystemUIFactory.getInstance().getRootComponent().createDependency()); + SystemUIFactory.getInstance().getSysUIComponent().createDependency()); Dependency.setInstance(mDependency); // TODO: Figure out another way to give reference to a SysuiTestableContext. diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java index 08e27841de03..b7175eabab39 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java @@ -74,7 +74,7 @@ public abstract class SysuiTestCase { public void SysuiSetup() throws Exception { SystemUIFactory.createFromConfig(mContext); mDependency = new TestableDependency( - SystemUIFactory.getInstance().getRootComponent().createDependency()); + SystemUIFactory.getInstance().getSysUIComponent().createDependency()); Dependency.setInstance(mDependency); mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(mContext, mock(Looper.class), mock(Executor.class), mock(DumpManager.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java index b7c198e53cfa..a6dfbbd93178 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java @@ -38,6 +38,7 @@ import androidx.test.filters.MediumTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.SysuiTestCase; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -94,6 +95,11 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { mContext, mController, newValueAnimator()); } + @After + public void tearDown() throws Exception { + mInstrumentation.runOnMainSync(() -> mController.deleteWindowMagnification()); + } + @Test public void enableWindowMagnification_disabled_expectedStartAndEndValues() { enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod); diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index 4fdc06e64e2c..8f082c15df36 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -27,6 +27,9 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -34,6 +37,8 @@ import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.content.pm.PackageManager; +import android.media.AudioManager; +import android.media.AudioRecordingConfiguration; import android.os.Looper; import android.os.UserHandle; import android.testing.AndroidTestingRunner; @@ -47,9 +52,11 @@ import com.android.systemui.dump.DumpManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Collections; import java.util.List; @SmallTest @@ -73,6 +80,12 @@ public class AppOpsControllerTest extends SysuiTestCase { private PermissionFlagsCache mFlagsCache; @Mock private PackageManager mPackageManager; + @Mock(stubOnly = true) + private AudioManager mAudioManager; + @Mock(stubOnly = true) + private AudioManager.AudioRecordingCallback mRecordingCallback; + @Mock(stubOnly = true) + private AudioRecordingConfiguration mPausedMockRecording; private AppOpsControllerImpl mController; private TestableLooper mTestableLooper; @@ -94,11 +107,20 @@ public class AppOpsControllerTest extends SysuiTestCase { when(mFlagsCache.getPermissionFlags(anyString(), anyString(), eq(TEST_UID_NON_USER_SENSITIVE))).thenReturn(0); + doAnswer((invocation) -> mRecordingCallback = invocation.getArgument(0)) + .when(mAudioManager).registerAudioRecordingCallback(any(), any()); + when(mPausedMockRecording.getClientUid()).thenReturn(TEST_UID); + when(mPausedMockRecording.isClientSilenced()).thenReturn(true); + + when(mAudioManager.getActiveRecordingConfigurations()) + .thenReturn(List.of(mPausedMockRecording)); + mController = new AppOpsControllerImpl( mContext, mTestableLooper.getLooper(), mDumpManager, - mFlagsCache + mFlagsCache, + mAudioManager ); } @@ -363,6 +385,89 @@ public class AppOpsControllerTest extends SysuiTestCase { AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); } + @Test + public void testPausedRecordingIsRetrievedOnCreation() { + mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); + mTestableLooper.processAllMessages(); + + mController.onOpActiveChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + + verify(mCallback, never()) + .onActiveStateChanged(anyInt(), anyInt(), anyString(), anyBoolean()); + } + + @Test + public void testPausedRecordingFilteredOut() { + mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); + mTestableLooper.processAllMessages(); + + mController.onOpActiveChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + + assertTrue(mController.getActiveAppOps().isEmpty()); + } + + @Test + public void testOnlyRecordAudioPaused() { + mController.addCallback(new int[]{ + AppOpsManager.OP_RECORD_AUDIO, + AppOpsManager.OP_CAMERA + }, mCallback); + mTestableLooper.processAllMessages(); + + mController.onOpActiveChanged( + AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + + verify(mCallback).onActiveStateChanged( + AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, true); + List<AppOpItem> list = mController.getActiveAppOps(); + + assertEquals(1, list.size()); + assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode()); + } + + @Test + public void testUnpausedRecordingSentActive() { + mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); + mTestableLooper.processAllMessages(); + mController.onOpActiveChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); + + mTestableLooper.processAllMessages(); + mRecordingCallback.onRecordingConfigChanged(Collections.emptyList()); + + mTestableLooper.processAllMessages(); + + verify(mCallback).onActiveStateChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); + } + + @Test + public void testAudioPausedSentInactive() { + mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); + mTestableLooper.processAllMessages(); + mController.onOpActiveChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + + AudioRecordingConfiguration mockARC = mock(AudioRecordingConfiguration.class); + when(mockARC.getClientUid()).thenReturn(TEST_UID_OTHER); + when(mockARC.isClientSilenced()).thenReturn(true); + + mRecordingCallback.onRecordingConfigChanged(List.of(mockARC)); + mTestableLooper.processAllMessages(); + + InOrder inOrder = inOrder(mCallback); + inOrder.verify(mCallback).onActiveStateChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + inOrder.verify(mCallback).onActiveStateChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, false); + } + private class TestHandler extends AppOpsControllerImpl.H { TestHandler(Looper looper) { mController.super(looper); diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java index afcd4414c667..7567f0cf04b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java @@ -41,7 +41,7 @@ import com.android.internal.app.AssistUtils; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; -import com.android.systemui.statusbar.phone.NavigationModeController; +import com.android.systemui.navigationbar.NavigationModeController; import org.junit.After; import org.junit.Before; diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index d4a94c5b9e66..c8566c599108 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -54,6 +54,7 @@ import android.testing.TestableLooper.RunWithLooper; import com.android.internal.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import org.junit.Before; @@ -103,8 +104,8 @@ public class AuthControllerTest extends SysuiTestCase { when(mDialog1.isAllowDeviceCredentials()).thenReturn(false); when(mDialog2.isAllowDeviceCredentials()).thenReturn(false); - mAuthController = new TestableAuthController( - context, mock(CommandQueue.class), new MockInjector()); + mAuthController = new TestableAuthController(context, mock(CommandQueue.class), + mock(StatusBarStateController.class), new MockInjector()); mAuthController.start(); } @@ -502,8 +503,9 @@ public class AuthControllerTest extends SysuiTestCase { private int mBuildCount = 0; private PromptInfo mLastBiometricPromptInfo; - TestableAuthController(Context context, CommandQueue commandQueue, Injector injector) { - super(context, commandQueue, injector); + TestableAuthController(Context context, CommandQueue commandQueue, + StatusBarStateController statusBarStateController, Injector injector) { + super(context, commandQueue, statusBarStateController, injector); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index 1538a32ecf8a..a7808ad54d63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -60,7 +60,6 @@ import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.statusbar.IStatusBarService; -import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dump.DumpManager; @@ -71,9 +70,7 @@ import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationRemoveInterceptor; -import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.RankingBuilder; -import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -87,7 +84,7 @@ import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.LockscreenLockIconController; import com.android.systemui.statusbar.phone.NotificationGroupManager; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; +import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl; import com.android.systemui.statusbar.phone.NotificationShadeWindowView; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.BatteryController; @@ -95,7 +92,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.FloatingContentCoordinator; -import com.android.systemui.util.InjectionInflationController; import com.google.common.collect.ImmutableList; @@ -159,7 +155,7 @@ public class BubbleControllerTest extends SysuiTestCase { private ArgumentCaptor<NotificationRemoveInterceptor> mRemoveInterceptorCaptor; private TestableBubbleController mBubbleController; - private NotificationShadeWindowController mNotificationShadeWindowController; + private NotificationShadeWindowControllerImpl mNotificationShadeWindowController; private NotificationEntryListener mEntryListener; private NotificationRemoveInterceptor mRemoveInterceptor; @@ -199,8 +195,6 @@ public class BubbleControllerTest extends SysuiTestCase { private TestableLooper mTestableLooper; - private SuperStatusBarViewFactory mSuperStatusBarViewFactory; - @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -210,24 +204,8 @@ public class BubbleControllerTest extends SysuiTestCase { mContext.addMockSystemService(FaceManager.class, mFaceManager); when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors); - mSuperStatusBarViewFactory = new SuperStatusBarViewFactory(mContext, - new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()), - new NotificationShelfComponent.Builder() { - @Override - public NotificationShelfComponent.Builder notificationShelf( - NotificationShelf view) { - return this; - } - - @Override - public NotificationShelfComponent build() { - return mNotificationShelfComponent; - } - }, - mLockIconController); - // Bubbles get added to status bar window view - mNotificationShadeWindowController = new NotificationShadeWindowController(mContext, + mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController, mColorExtractor, mDumpManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java index 0e7cb79ec266..4936360756fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java @@ -83,7 +83,7 @@ import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.LockscreenLockIconController; import com.android.systemui.statusbar.phone.NotificationGroupManager; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; +import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl; import com.android.systemui.statusbar.phone.NotificationShadeWindowView; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.BatteryController; @@ -153,7 +153,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor; private TestableBubbleController mBubbleController; - private NotificationShadeWindowController mNotificationShadeWindowController; + private NotificationShadeWindowControllerImpl mNotificationShadeWindowController; private NotifCollectionListener mEntryListener; private NotificationTestHelper mNotificationTestHelper; private ExpandableNotificationRow mRow; @@ -201,7 +201,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors); mSuperStatusBarViewFactory = new SuperStatusBarViewFactory(mContext, - new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()), + new InjectionInflationController(SystemUIFactory.getInstance().getSysUIComponent() + .createViewInstanceCreatorFactory()), new NotificationShelfComponent.Builder() { @Override public NotificationShelfComponent.Builder notificationShelf( @@ -217,7 +218,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mLockIconController); // Bubbles get added to status bar window view - mNotificationShadeWindowController = new NotificationShadeWindowController(mContext, + mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController, mColorExtractor, mDumpManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java index 0a6d0716cb85..51ca2a4e5966 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java @@ -27,11 +27,11 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.phone.NotificationGroupManager; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ZenModeController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java index 4c5495474018..c8e0f490a783 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -71,7 +71,7 @@ import com.android.systemui.plugins.GlobalActions; import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.settings.CurrentUserContextTracker; import com.android.systemui.statusbar.NotificationShadeDepthController; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.RingerModeLiveData; diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java index f70fb4f55a8d..d1f505baa9e3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java @@ -254,16 +254,12 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { int mCleanDateFormatInvokations; private int mCounter; - Uri getUri() { - return mSliceUri; - } + TestableKeyguardSliceProvider() { + super(); - @Override - protected void inject() { mAlarmManager = KeyguardSliceProviderTest.this.mAlarmManager; mContentResolver = KeyguardSliceProviderTest.this.mContentResolver; mZenModeController = KeyguardSliceProviderTest.this.mZenModeController; - mMediaWakeLock = KeyguardSliceProviderTest.this.mMediaWakeLock; mDozeParameters = KeyguardSliceProviderTest.this.mDozeParameters; mNextAlarmController = KeyguardSliceProviderTest.this.mNextAlarmController; mStatusBarStateController = KeyguardSliceProviderTest.this.mStatusBarStateController; @@ -272,6 +268,17 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { } @Override + public boolean onCreateSliceProvider() { + boolean result = super.onCreateSliceProvider(); + mMediaWakeLock = KeyguardSliceProviderTest.this.mMediaWakeLock; + return result; + } + + Uri getUri() { + return mSliceUri; + } + + @Override protected boolean isDndOn() { return mIsZenMode; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 90e9ef4e9c56..c874b1fb72ad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -42,10 +42,11 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.dump.DumpManager; -import com.android.systemui.statusbar.phone.NavigationModeController; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.DeviceConfigProxyFake; +import com.android.systemui.util.InjectionInflationController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -71,6 +72,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock PowerManager mPowerManager; private @Mock TrustManager mTrustManager; private @Mock NavigationModeController mNavigationModeController; + private @Mock InjectionInflationController mInjectionInflationController; private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake(); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -88,7 +90,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mContext, mFalsingManager, mLockPatternUtils, mBroadcastDispatcher, () -> mStatusBarKeyguardViewManager, mDismissCallbackRegistry, mUpdateMonitor, mDumpManager, mUiBgExecutor, - mPowerManager, mTrustManager, mDeviceConfig, mNavigationModeController); + mPowerManager, mTrustManager, mDeviceConfig, mNavigationModeController, + mInjectionInflationController); mViewMediator.start(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java index 492b33e3c4a6..89538ac8bc9f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java @@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @@ -34,19 +33,23 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import java.util.ArrayList; -import java.util.Map; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper public class MediaDataCombineLatestTest extends SysuiTestCase { + @Rule public MockitoRule mockito = MockitoJUnit.rule(); + private static final String KEY = "TEST_KEY"; private static final String OLD_KEY = "TEST_KEY_OLD"; private static final String APP = "APP"; @@ -59,39 +62,26 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { private MediaDataCombineLatest mManager; - @Mock private MediaDataManager mDataSource; - @Mock private MediaDeviceManager mDeviceSource; @Mock private MediaDataManager.Listener mListener; - private MediaDataManager.Listener mDataListener; - private MediaDeviceManager.Listener mDeviceListener; - private MediaData mMediaData; private MediaDeviceData mDeviceData; @Before public void setUp() { - mDataSource = mock(MediaDataManager.class); - mDeviceSource = mock(MediaDeviceManager.class); - mListener = mock(MediaDataManager.Listener.class); - - mManager = new MediaDataCombineLatest(mDataSource, mDeviceSource); - - mDataListener = captureDataListener(); - mDeviceListener = captureDeviceListener(); - + mManager = new MediaDataCombineLatest(); mManager.addListener(mListener); mMediaData = new MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, - new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, false, - KEY, false); + new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, true, + false, KEY, false); mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME); } @Test public void eventNotEmittedWithoutDevice() { // WHEN data source emits an event without device data - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); + mManager.onMediaDataLoaded(KEY, null, mMediaData); // THEN an event isn't emitted verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any()); } @@ -99,7 +89,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void eventNotEmittedWithoutMedia() { // WHEN device source emits an event without media data - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); + mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // THEN an event isn't emitted verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any()); } @@ -107,9 +97,9 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void emitEventAfterDeviceFirst() { // GIVEN that a device event has already been received - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); + mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // WHEN media event is received - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); + mManager.onMediaDataLoaded(KEY, null, mMediaData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture()); @@ -119,9 +109,9 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void emitEventAfterMediaFirst() { // GIVEN that media event has already been received - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); + mManager.onMediaDataLoaded(KEY, null, mMediaData); // WHEN device event is received - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); + mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture()); @@ -131,11 +121,11 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void migrateKeyMediaFirst() { // GIVEN that media and device info has already been received - mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); reset(mListener); // WHEN a key migration event is received - mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); + mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture()); @@ -145,11 +135,11 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void migrateKeyDeviceFirst() { // GIVEN that media and device info has already been received - mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); reset(mListener); // WHEN a key migration event is received - mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); + mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture()); @@ -159,12 +149,12 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void migrateKeyMediaAfter() { // GIVEN that media and device info has already been received - mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); - mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); reset(mListener); // WHEN a second key migration event is received for media - mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); + mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); // THEN the key has already been migrated ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture()); @@ -174,12 +164,12 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void migrateKeyDeviceAfter() { // GIVEN that media and device info has already been received - mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); - mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData); + mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); + mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData); reset(mListener); // WHEN a second key migration event is received for the device - mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); + mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); // THEN the key has already be migrated ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture()); @@ -189,60 +179,34 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void mediaDataRemoved() { // WHEN media data is removed without first receiving device or data - mDataListener.onMediaDataRemoved(KEY); + mManager.onMediaDataRemoved(KEY); // THEN a removed event isn't emitted verify(mListener, never()).onMediaDataRemoved(eq(KEY)); } @Test public void mediaDataRemovedAfterMediaEvent() { - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); - mDataListener.onMediaDataRemoved(KEY); + mManager.onMediaDataLoaded(KEY, null, mMediaData); + mManager.onMediaDataRemoved(KEY); verify(mListener).onMediaDataRemoved(eq(KEY)); } @Test public void mediaDataRemovedAfterDeviceEvent() { - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); - mDataListener.onMediaDataRemoved(KEY); + mManager.onMediaDeviceChanged(KEY, null, mDeviceData); + mManager.onMediaDataRemoved(KEY); verify(mListener).onMediaDataRemoved(eq(KEY)); } @Test public void mediaDataKeyUpdated() { // GIVEN that device and media events have already been received - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); + mManager.onMediaDataLoaded(KEY, null, mMediaData); + mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // WHEN the key is changed - mDataListener.onMediaDataLoaded("NEW_KEY", KEY, mMediaData); + mManager.onMediaDataLoaded("NEW_KEY", KEY, mMediaData); // THEN the listener gets a load event with the correct keys ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded(eq("NEW_KEY"), any(), captor.capture()); } - - @Test - public void getDataIncludesDevice() { - // GIVEN that device and media events have been received - mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData); - mDataListener.onMediaDataLoaded(KEY, null, mMediaData); - - // THEN the result of getData includes device info - Map<String, MediaData> results = mManager.getData(); - assertThat(results.get(KEY)).isNotNull(); - assertThat(results.get(KEY).getDevice()).isEqualTo(mDeviceData); - } - - private MediaDataManager.Listener captureDataListener() { - ArgumentCaptor<MediaDataManager.Listener> captor = ArgumentCaptor.forClass( - MediaDataManager.Listener.class); - verify(mDataSource).addListener(captor.capture()); - return captor.getValue(); - } - - private MediaDeviceManager.Listener captureDeviceListener() { - ArgumentCaptor<MediaDeviceManager.Listener> captor = ArgumentCaptor.forClass( - MediaDeviceManager.Listener.class); - verify(mDeviceSource).addListener(captor.capture()); - return captor.getValue(); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt index afb64a7649b4..36b6527167f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt @@ -32,6 +32,7 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.`when` import org.mockito.Mockito.never +import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import java.util.concurrent.Executor @@ -56,8 +57,6 @@ private fun <T> any(): T = Mockito.any() class MediaDataFilterTest : SysuiTestCase() { @Mock - private lateinit var combineLatest: MediaDataCombineLatest - @Mock private lateinit var listener: MediaDataManager.Listener @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher @@ -78,8 +77,9 @@ class MediaDataFilterTest : SysuiTestCase() { @Before fun setup() { MockitoAnnotations.initMocks(this) - mediaDataFilter = MediaDataFilter(combineLatest, broadcastDispatcher, mediaResumeListener, - mediaDataManager, lockscreenUserManager, executor) + mediaDataFilter = MediaDataFilter(broadcastDispatcher, mediaResumeListener, + lockscreenUserManager, executor) + mediaDataFilter.mediaDataManager = mediaDataManager mediaDataFilter.addListener(listener) // Start all tests as main user @@ -152,8 +152,9 @@ class MediaDataFilterTest : SysuiTestCase() { @Test fun testOnUserSwitched_addsNewUserControls() { // GIVEN that we had some media for both users - val dataMap = mapOf(KEY to dataMain, KEY_ALT to dataGuest) - `when`(combineLatest.getData()).thenReturn(dataMap) + mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain) + mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataGuest) + reset(listener) // and we switch to guest user setUser(USER_GUEST) @@ -213,4 +214,4 @@ class MediaDataFilterTest : SysuiTestCase() { verify(mediaDataManager).setTimedOut(eq(KEY), eq(true)) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 3789e6ef1f65..84c1bf932b5e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -13,8 +13,10 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dump.DumpManager +import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.SbnBuilder import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -23,9 +25,13 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit import org.mockito.Mockito.`when` as whenever @@ -47,6 +53,7 @@ private fun <T> anyObject(): T { @RunWith(AndroidTestingRunner::class) class MediaDataManagerTest : SysuiTestCase() { + @JvmField @Rule val mockito = MockitoJUnit.rule() @Mock lateinit var mediaControllerFactory: MediaControllerFactory @Mock lateinit var controller: MediaController lateinit var session: MediaSession @@ -57,19 +64,38 @@ class MediaDataManagerTest : SysuiTestCase() { @Mock lateinit var broadcastDispatcher: BroadcastDispatcher @Mock lateinit var mediaTimeoutListener: MediaTimeoutListener @Mock lateinit var mediaResumeListener: MediaResumeListener + @Mock lateinit var mediaSessionBasedFilter: MediaSessionBasedFilter + @Mock lateinit var mediaDeviceManager: MediaDeviceManager + @Mock lateinit var mediaDataCombineLatest: MediaDataCombineLatest + @Mock lateinit var mediaDataFilter: MediaDataFilter + @Mock lateinit var listener: MediaDataManager.Listener @Mock lateinit var pendingIntent: PendingIntent - @JvmField @Rule val mockito = MockitoJUnit.rule() + @Mock lateinit var activityStarter: ActivityStarter lateinit var mediaDataManager: MediaDataManager lateinit var mediaNotification: StatusBarNotification + @Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData> @Before fun setup() { foregroundExecutor = FakeExecutor(FakeSystemClock()) backgroundExecutor = FakeExecutor(FakeSystemClock()) - mediaDataManager = MediaDataManager(context, backgroundExecutor, foregroundExecutor, - mediaControllerFactory, broadcastDispatcher, dumpManager, - mediaTimeoutListener, mediaResumeListener, useMediaResumption = true, - useQsMediaPlayer = true) + mediaDataManager = MediaDataManager( + context = context, + backgroundExecutor = backgroundExecutor, + foregroundExecutor = foregroundExecutor, + mediaControllerFactory = mediaControllerFactory, + broadcastDispatcher = broadcastDispatcher, + dumpManager = dumpManager, + mediaTimeoutListener = mediaTimeoutListener, + mediaResumeListener = mediaResumeListener, + mediaSessionBasedFilter = mediaSessionBasedFilter, + mediaDeviceManager = mediaDeviceManager, + mediaDataCombineLatest = mediaDataCombineLatest, + mediaDataFilter = mediaDataFilter, + activityStarter = activityStarter, + useMediaResumption = true, + useQsMediaPlayer = true + ) session = MediaSession(context, "MediaDataManagerTestSession") mediaNotification = SbnBuilder().run { setPkg(PACKAGE_NAME) @@ -84,6 +110,12 @@ class MediaDataManagerTest : SysuiTestCase() { putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE) } whenever(mediaControllerFactory.create(eq(session.sessionToken))).thenReturn(controller) + + // This is an ugly hack for now. The mediaSessionBasedFilter is one of the internal + // listeners in the internal processing pipeline. It receives events, but ince it is a + // mock, it doesn't pass those events along the chain to the external listeners. So, just + // treat mediaSessionBasedFilter as a listener for testing. + listener = mediaSessionBasedFilter } @After @@ -113,8 +145,6 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testOnMetaDataLoaded_callsListener() { - val listener = mock(MediaDataManager.Listener::class.java) - mediaDataManager.addListener(listener) mediaDataManager.onNotificationAdded(KEY, mediaNotification) mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java)) verify(listener).onMediaDataLoaded(eq(KEY), eq(null), anyObject()) @@ -122,84 +152,123 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testOnMetaDataLoaded_conservesActiveFlag() { - val listener = TestListener() whenever(mediaControllerFactory.create(anyObject())).thenReturn(controller) whenever(controller.metadata).thenReturn(metadataBuilder.build()) mediaDataManager.addListener(listener) mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - assertThat(listener.data!!.active).isTrue() + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) + assertThat(mediaDataCaptor.value!!.active).isTrue() } @Test fun testOnNotificationRemoved_callsListener() { - val listener = mock(MediaDataManager.Listener::class.java) - mediaDataManager.addListener(listener) mediaDataManager.onNotificationAdded(KEY, mediaNotification) mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java)) mediaDataManager.onNotificationRemoved(KEY) - verify(listener).onMediaDataRemoved(eq(KEY)) } @Test fun testOnNotificationRemoved_withResumption() { // GIVEN that the manager has a notification with a resume action - val listener = TestListener() - mediaDataManager.addListener(listener) whenever(controller.metadata).thenReturn(metadataBuilder.build()) mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - val data = listener.data!! + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) + val data = mediaDataCaptor.value assertThat(data.resumption).isFalse() mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) // WHEN the notification is removed mediaDataManager.onNotificationRemoved(KEY) // THEN the media data indicates that it is for resumption - assertThat(listener.data!!.resumption).isTrue() - // AND the new key is the package name - assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) - assertThat(listener.oldKey!!).isEqualTo(KEY) - assertThat(listener.removedKey).isNull() + verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor)) + assertThat(mediaDataCaptor.value.resumption).isTrue() } @Test fun testOnNotificationRemoved_twoWithResumption() { // GIVEN that the manager has two notifications with resume actions - val listener = TestListener() - mediaDataManager.addListener(listener) whenever(controller.metadata).thenReturn(metadataBuilder.build()) mediaDataManager.onNotificationAdded(KEY, mediaNotification) mediaDataManager.onNotificationAdded(KEY_2, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(2) assertThat(foregroundExecutor.runAllReady()).isEqualTo(2) - val data = listener.data!! + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) + val data = mediaDataCaptor.value assertThat(data.resumption).isFalse() val resumableData = data.copy(resumeAction = Runnable {}) mediaDataManager.onMediaDataLoaded(KEY, null, resumableData) mediaDataManager.onMediaDataLoaded(KEY_2, null, resumableData) + reset(listener) // WHEN the first is removed mediaDataManager.onNotificationRemoved(KEY) // THEN the data is for resumption and the key is migrated to the package name - assertThat(listener.data!!.resumption).isTrue() - assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) - assertThat(listener.oldKey!!).isEqualTo(KEY) - assertThat(listener.removedKey).isNull() + verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor)) + assertThat(mediaDataCaptor.value.resumption).isTrue() + verify(listener, never()).onMediaDataRemoved(eq(KEY)) // WHEN the second is removed mediaDataManager.onNotificationRemoved(KEY_2) // THEN the data is for resumption and the second key is removed - assertThat(listener.data!!.resumption).isTrue() - assertThat(listener.key!!).isEqualTo(PACKAGE_NAME) - assertThat(listener.oldKey!!).isEqualTo(PACKAGE_NAME) - assertThat(listener.removedKey!!).isEqualTo(KEY_2) + verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(PACKAGE_NAME), + capture(mediaDataCaptor)) + assertThat(mediaDataCaptor.value.resumption).isTrue() + verify(listener).onMediaDataRemoved(eq(KEY_2)) + } + + @Test + fun testAppBlockedFromResumption() { + // GIVEN that the manager has a notification with a resume action + whenever(controller.metadata).thenReturn(metadataBuilder.build()) + mediaDataManager.onNotificationAdded(KEY, mediaNotification) + assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) + val data = mediaDataCaptor.value + assertThat(data.resumption).isFalse() + mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) + + // and the manager should block the package from creating resume controls + val blocked = mutableSetOf(PACKAGE_NAME, "com.example.app") + mediaDataManager.appsBlockedFromResume = blocked + + // WHEN the notification is removed + mediaDataManager.onNotificationRemoved(KEY) + + // THEN the media data is removed + verify(listener).onMediaDataRemoved(eq(KEY)) + } + + @Test + fun testAppUnblockedFromResumption() { + // GIVEN that an app was blocked from resuming + val blocked = mutableSetOf(PACKAGE_NAME, "com.example.app") + mediaDataManager.appsBlockedFromResume = blocked + + // and GIVEN that the manager has a notification from that app with a resume action + whenever(controller.metadata).thenReturn(metadataBuilder.build()) + mediaDataManager.onNotificationAdded(KEY, mediaNotification) + assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) + val data = mediaDataCaptor.value + assertThat(data.resumption).isFalse() + mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) + + // WHEN the app is unblocked + mediaDataManager.appsBlockedFromResume = mutableSetOf("com.example.app") + + // and the notification is removed + mediaDataManager.onNotificationRemoved(KEY) + + // THEN the entry will stay as a resume control + verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor)) } @Test fun testAddResumptionControls() { - val listener = TestListener() - mediaDataManager.addListener(listener) // WHEN resumption controls are added` val desc = MediaDescription.Builder().run { setTitle(SESSION_TITLE) @@ -210,7 +279,8 @@ class MediaDataManagerTest : SysuiTestCase() { assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) // THEN the media data indicates that it is for resumption - val data = listener.data!! + verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(null), capture(mediaDataCaptor)) + val data = mediaDataCaptor.value assertThat(data.resumption).isTrue() assertThat(data.song).isEqualTo(SESSION_TITLE) assertThat(data.app).isEqualTo(APP_NAME) @@ -219,8 +289,6 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testDismissMedia_listenerCalled() { - val listener = mock(MediaDataManager.Listener::class.java) - mediaDataManager.addListener(listener) mediaDataManager.onNotificationAdded(KEY, mediaNotification) mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java)) mediaDataManager.dismissMediaData(KEY, 0L) @@ -230,26 +298,4 @@ class MediaDataManagerTest : SysuiTestCase() { verify(listener).onMediaDataRemoved(eq(KEY)) } - - /** - * Simple implementation of [MediaDataManager.Listener] for the test. - * - * Giving up on trying to get a mock Listener and ArgumentCaptor to work. - */ - private class TestListener : MediaDataManager.Listener { - var data: MediaData? = null - var key: String? = null - var oldKey: String? = null - var removedKey: String? = null - - override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { - this.key = key - this.oldKey = oldKey - this.data = data - } - - override fun onMediaDataRemoved(key: String) { - removedKey = key - } - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt index 7bc15dd46cd6..fdb432cc097c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt @@ -68,7 +68,6 @@ private fun <T> eq(value: T): T = Mockito.eq(value) ?: value public class MediaDeviceManagerTest : SysuiTestCase() { private lateinit var manager: MediaDeviceManager - @Mock private lateinit var mediaDataManager: MediaDataManager @Mock private lateinit var lmmFactory: LocalMediaManagerFactory @Mock private lateinit var lmm: LocalMediaManager @Mock private lateinit var mr2: MediaRouter2Manager @@ -91,7 +90,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() { fakeFgExecutor = FakeExecutor(FakeSystemClock()) fakeBgExecutor = FakeExecutor(FakeSystemClock()) manager = MediaDeviceManager(context, lmmFactory, mr2, fakeFgExecutor, fakeBgExecutor, - mediaDataManager, dumpster) + dumpster) manager.addListener(listener) // Configure mocks. diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt new file mode 100644 index 000000000000..ca8f79d08ef0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.media + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` as whenever + +@SmallTest +@RunWith(AndroidTestingRunner::class) +public class MediaPlayerDataTest : SysuiTestCase() { + + companion object { + val LOCAL = true + val RESUMPTION = true + } + + @Before + fun setup() { + MediaPlayerData.clear() + } + + @Test + fun addPlayingThenRemote() { + val playerIsPlaying = mock(MediaControlPanel::class.java) + whenever(playerIsPlaying.isPlaying).thenReturn(true) + val dataIsPlaying = createMediaData("app1", LOCAL, !RESUMPTION) + + val playerIsRemote = mock(MediaControlPanel::class.java) + whenever(playerIsRemote.isPlaying).thenReturn(false) + val dataIsRemote = createMediaData("app2", !LOCAL, !RESUMPTION) + + MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying) + MediaPlayerData.addMediaPlayer("2", dataIsRemote, playerIsRemote) + + val players = MediaPlayerData.players() + assertThat(players).hasSize(2) + assertThat(players).containsExactly(playerIsPlaying, playerIsRemote).inOrder() + } + + @Test + fun switchPlayersPlaying() { + val playerIsPlaying1 = mock(MediaControlPanel::class.java) + whenever(playerIsPlaying1.isPlaying).thenReturn(true) + val dataIsPlaying1 = createMediaData("app1", LOCAL, !RESUMPTION) + + val playerIsPlaying2 = mock(MediaControlPanel::class.java) + whenever(playerIsPlaying2.isPlaying).thenReturn(false) + val dataIsPlaying2 = createMediaData("app2", LOCAL, !RESUMPTION) + + MediaPlayerData.addMediaPlayer("1", dataIsPlaying1, playerIsPlaying1) + MediaPlayerData.addMediaPlayer("2", dataIsPlaying2, playerIsPlaying2) + + whenever(playerIsPlaying1.isPlaying).thenReturn(false) + whenever(playerIsPlaying2.isPlaying).thenReturn(true) + + MediaPlayerData.addMediaPlayer("1", dataIsPlaying1, playerIsPlaying1) + MediaPlayerData.addMediaPlayer("2", dataIsPlaying2, playerIsPlaying2) + + val players = MediaPlayerData.players() + assertThat(players).hasSize(2) + assertThat(players).containsExactly(playerIsPlaying2, playerIsPlaying1).inOrder() + } + + @Test + fun fullOrderTest() { + val playerIsPlaying = mock(MediaControlPanel::class.java) + whenever(playerIsPlaying.isPlaying).thenReturn(true) + val dataIsPlaying = createMediaData("app1", LOCAL, !RESUMPTION) + + val playerIsPlayingAndRemote = mock(MediaControlPanel::class.java) + whenever(playerIsPlayingAndRemote.isPlaying).thenReturn(true) + val dataIsPlayingAndRemote = createMediaData("app2", !LOCAL, !RESUMPTION) + + val playerIsStoppedAndLocal = mock(MediaControlPanel::class.java) + whenever(playerIsStoppedAndLocal.isPlaying).thenReturn(false) + val dataIsStoppedAndLocal = createMediaData("app3", LOCAL, !RESUMPTION) + + val playerIsStoppedAndRemote = mock(MediaControlPanel::class.java) + whenever(playerIsStoppedAndLocal.isPlaying).thenReturn(false) + val dataIsStoppedAndRemote = createMediaData("app4", !LOCAL, !RESUMPTION) + + val playerCanResume = mock(MediaControlPanel::class.java) + whenever(playerCanResume.isPlaying).thenReturn(false) + val dataCanResume = createMediaData("app5", LOCAL, RESUMPTION) + + MediaPlayerData.addMediaPlayer("3", dataIsStoppedAndLocal, playerIsStoppedAndLocal) + MediaPlayerData.addMediaPlayer("5", dataIsStoppedAndRemote, playerIsStoppedAndRemote) + MediaPlayerData.addMediaPlayer("4", dataCanResume, playerCanResume) + MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying) + MediaPlayerData.addMediaPlayer("2", dataIsPlayingAndRemote, playerIsPlayingAndRemote) + + val players = MediaPlayerData.players() + assertThat(players).hasSize(5) + assertThat(players).containsExactly(playerIsPlaying, playerIsPlayingAndRemote, + playerIsStoppedAndLocal, playerCanResume, playerIsStoppedAndRemote).inOrder() + } + + private fun createMediaData(app: String, isLocalSession: Boolean, resumption: Boolean) = + MediaData(0, false, 0, app, null, null, null, null, emptyList(), emptyList<Int>(), "", + null, null, null, true, null, isLocalSession, resumption, null, false) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt new file mode 100644 index 000000000000..887cc777d4fe --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.media + +import android.graphics.Color +import android.media.session.MediaController +import android.media.session.MediaController.PlaybackInfo +import android.media.session.MediaSession +import android.media.session.MediaSessionManager +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest + +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.time.FakeSystemClock + +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.any +import org.mockito.Mockito.never +import org.mockito.Mockito.reset +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit +import org.mockito.Mockito.`when` as whenever + +private const val PACKAGE = "PKG" +private const val KEY = "TEST_KEY" +private const val NOTIF_KEY = "TEST_KEY" +private const val SESSION_ARTIST = "SESSION_ARTIST" +private const val SESSION_TITLE = "SESSION_TITLE" +private const val APP_NAME = "APP_NAME" +private const val USER_ID = 0 + +private val info = MediaData( + userId = USER_ID, + initialized = true, + backgroundColor = Color.DKGRAY, + app = APP_NAME, + appIcon = null, + artist = SESSION_ARTIST, + song = SESSION_TITLE, + artwork = null, + actions = emptyList(), + actionsToShowInCompact = emptyList(), + packageName = PACKAGE, + token = null, + clickIntent = null, + device = null, + active = true, + resumeAction = null, + resumption = false, + notificationKey = NOTIF_KEY, + hasCheckedForResume = false +) + +private fun <T> eq(value: T): T = Mockito.eq(value) ?: value + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +public class MediaSessionBasedFilterTest : SysuiTestCase() { + + @JvmField @Rule val mockito = MockitoJUnit.rule() + + // Unit to be tested + private lateinit var filter: MediaSessionBasedFilter + + private lateinit var sessionListener: MediaSessionManager.OnActiveSessionsChangedListener + @Mock private lateinit var mediaListener: MediaDataManager.Listener + + // MediaSessionBasedFilter dependencies + @Mock private lateinit var mediaSessionManager: MediaSessionManager + private lateinit var fgExecutor: FakeExecutor + private lateinit var bgExecutor: FakeExecutor + + @Mock private lateinit var controller1: MediaController + @Mock private lateinit var controller2: MediaController + @Mock private lateinit var controller3: MediaController + @Mock private lateinit var controller4: MediaController + + private lateinit var token1: MediaSession.Token + private lateinit var token2: MediaSession.Token + private lateinit var token3: MediaSession.Token + private lateinit var token4: MediaSession.Token + + @Mock private lateinit var remotePlaybackInfo: PlaybackInfo + @Mock private lateinit var localPlaybackInfo: PlaybackInfo + + private lateinit var session1: MediaSession + private lateinit var session2: MediaSession + private lateinit var session3: MediaSession + private lateinit var session4: MediaSession + + private lateinit var mediaData1: MediaData + private lateinit var mediaData2: MediaData + private lateinit var mediaData3: MediaData + private lateinit var mediaData4: MediaData + + @Before + fun setUp() { + fgExecutor = FakeExecutor(FakeSystemClock()) + bgExecutor = FakeExecutor(FakeSystemClock()) + filter = MediaSessionBasedFilter(context, mediaSessionManager, fgExecutor, bgExecutor) + + // Configure mocks. + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(emptyList()) + + session1 = MediaSession(context, "MediaSessionBasedFilter1") + session2 = MediaSession(context, "MediaSessionBasedFilter2") + session3 = MediaSession(context, "MediaSessionBasedFilter3") + session4 = MediaSession(context, "MediaSessionBasedFilter4") + + token1 = session1.sessionToken + token2 = session2.sessionToken + token3 = session3.sessionToken + token4 = session4.sessionToken + + whenever(controller1.getSessionToken()).thenReturn(token1) + whenever(controller2.getSessionToken()).thenReturn(token2) + whenever(controller3.getSessionToken()).thenReturn(token3) + whenever(controller4.getSessionToken()).thenReturn(token4) + + whenever(controller1.getPackageName()).thenReturn(PACKAGE) + whenever(controller2.getPackageName()).thenReturn(PACKAGE) + whenever(controller3.getPackageName()).thenReturn(PACKAGE) + whenever(controller4.getPackageName()).thenReturn(PACKAGE) + + mediaData1 = info.copy(token = token1) + mediaData2 = info.copy(token = token2) + mediaData3 = info.copy(token = token3) + mediaData4 = info.copy(token = token4) + + whenever(remotePlaybackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE) + whenever(localPlaybackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL) + + whenever(controller1.getPlaybackInfo()).thenReturn(localPlaybackInfo) + whenever(controller2.getPlaybackInfo()).thenReturn(localPlaybackInfo) + whenever(controller3.getPlaybackInfo()).thenReturn(localPlaybackInfo) + whenever(controller4.getPlaybackInfo()).thenReturn(localPlaybackInfo) + + // Capture listener + bgExecutor.runAllReady() + val listenerCaptor = ArgumentCaptor.forClass( + MediaSessionManager.OnActiveSessionsChangedListener::class.java) + verify(mediaSessionManager).addOnActiveSessionsChangedListener( + listenerCaptor.capture(), any()) + sessionListener = listenerCaptor.value + + filter.addListener(mediaListener) + } + + @After + fun tearDown() { + session1.release() + session2.release() + session3.release() + session4.release() + } + + @Test + fun noMediaSession_loadedEventNotFiltered() { + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + } + + @Test + fun noMediaSession_removedEventNotFiltered() { + filter.onMediaDataRemoved(KEY) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + verify(mediaListener).onMediaDataRemoved(eq(KEY)) + } + + @Test + fun matchingMediaSession_loadedEventNotFiltered() { + // GIVEN an active session + val controllers = listOf(controller1) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the session + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + } + + @Test + fun matchingMediaSession_removedEventNotFiltered() { + // GIVEN an active session + val controllers = listOf(controller1) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a removed event is received + filter.onMediaDataRemoved(KEY) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataRemoved(eq(KEY)) + } + + @Test + fun remoteSession_loadedEventNotFiltered() { + // GIVEN a remove session + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matche the session + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + } + + @Test + fun remoteAndLocalSessions_localLoadedEventFiltered() { + // GIVEN remote and local sessions + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the remote session + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(KEY, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is filtered + verify(mediaListener, never()).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2)) + } + + @Test + fun remoteAndLocalHaveDifferentKeys_localLoadedEventFiltered() { + // GIVEN remote and local sessions + val key1 = "KEY_1" + val key2 = "KEY_2" + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the remote session + filter.onMediaDataLoaded(key1, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1)) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(key2, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is filtered + verify(mediaListener, never()).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2)) + // AND there should be a removed event for key2 + verify(mediaListener).onMediaDataRemoved(eq(key2)) + } + + @Test + fun multipleRemoteSessions_loadedEventNotFiltered() { + // GIVEN two remote sessions + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + whenever(controller2.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the remote session + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(KEY, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2)) + } + + @Test + fun multipleOtherSessions_loadedEventNotFiltered() { + // GIVEN multiple active sessions from other packages + val controllers = listOf(controller1, controller2, controller3, controller4) + whenever(controller1.getPackageName()).thenReturn("PKG_1") + whenever(controller2.getPackageName()).thenReturn("PKG_2") + whenever(controller3.getPackageName()).thenReturn("PKG_3") + whenever(controller4.getPackageName()).thenReturn("PKG_4") + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received + filter.onMediaDataLoaded(KEY, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the event is not filtered + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) + } + + @Test + fun doNotFilterDuringKeyMigration() { + val key1 = "KEY_1" + val key2 = "KEY_2" + // GIVEN a loaded event + filter.onMediaDataLoaded(key1, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + reset(mediaListener) + // GIVEN remote and local sessions + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // WHEN a loaded event is received that matches the local session but it is a key migration + filter.onMediaDataLoaded(key2, key1, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the key migration event is fired + verify(mediaListener).onMediaDataLoaded(eq(key2), eq(key1), eq(mediaData2)) + } + + @Test + fun filterAfterKeyMigration() { + val key1 = "KEY_1" + val key2 = "KEY_2" + // GIVEN a loaded event + filter.onMediaDataLoaded(key1, null, mediaData1) + filter.onMediaDataLoaded(key1, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + reset(mediaListener) + // GIVEN remote and local sessions + whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) + val controllers = listOf(controller1, controller2) + whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) + sessionListener.onActiveSessionsChanged(controllers) + // GIVEN that the keys have been migrated + filter.onMediaDataLoaded(key2, key1, mediaData1) + filter.onMediaDataLoaded(key2, key1, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + reset(mediaListener) + // WHEN a loaded event is received that matches the local session + filter.onMediaDataLoaded(key2, null, mediaData2) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the key migration event is filtered + verify(mediaListener, never()).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2)) + // WHEN a loaded event is received that matches the remote session + filter.onMediaDataLoaded(key2, null, mediaData1) + bgExecutor.runAllReady() + fgExecutor.runAllReady() + // THEN the key migration event is fired + verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData1)) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java index aae075777506..37b7cbeb8ad6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -35,6 +35,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestableContext; import com.android.systemui.assist.AssistManager; +import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -65,6 +66,7 @@ public class NavigationBarButtonTest extends SysuiTestCase { mDependency.injectMockDependency(AssistManager.class); mDependency.injectMockDependency(OverviewProxyService.class); mDependency.injectMockDependency(KeyguardStateController.class); + mDependency.injectMockDependency(NavigationBarController.class); mNavBar = new NavigationBarView(context, null); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java index 3c66ac6acd04..2e4d8a7ff472 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NavigationBarControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.navigationbar; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; @@ -36,12 +36,32 @@ import static org.mockito.Mockito.verify; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import android.util.SparseArray; +import android.view.WindowManager; +import android.view.accessibility.AccessibilityManager; import androidx.test.filters.SmallTest; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.phone.NavigationBarFragment; +import com.android.systemui.accessibility.SystemActions; +import com.android.systemui.assist.AssistManager; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.model.SysUiState; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.recents.OverviewProxyService; +import com.android.systemui.recents.Recents; +import com.android.systemui.stackdivider.Divider; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; + +import java.util.Optional; import org.junit.After; import org.junit.Before; @@ -55,27 +75,47 @@ import org.junit.runner.RunWith; public class NavigationBarControllerTest extends SysuiTestCase { private NavigationBarController mNavigationBarController; - private NavigationBarFragment mDefaultNavBar; - private NavigationBarFragment mSecondaryNavBar; + private NavigationBar mDefaultNavBar; + private NavigationBar mSecondaryNavBar; private static final int SECONDARY_DISPLAY = 1; @Before public void setUp() { mNavigationBarController = spy( - new NavigationBarController(mContext, Dependency.get(Dependency.MAIN_HANDLER), - mock(CommandQueue.class))); + new NavigationBarController(mContext, + mock(WindowManager.class), + () -> mock(AssistManager.class), + mock(AccessibilityManager.class), + mock(AccessibilityManagerWrapper.class), + mock(DeviceProvisionedController.class), + mock(MetricsLogger.class), + mock(OverviewProxyService.class), + mock(NavigationModeController.class), + mock(StatusBarStateController.class), + mock(SysUiState.class), + mock(BroadcastDispatcher.class), + mock(CommandQueue.class), + mock(Divider.class), + Optional.of(mock(Recents.class)), + () -> mock(StatusBar.class), + mock(ShadeController.class), + mock(NotificationRemoteInputManager.class), + mock(SystemActions.class), + Dependency.get(Dependency.MAIN_HANDLER), + mock(UiEventLogger.class), + mock(ConfigurationController.class))); initializeNavigationBars(); } private void initializeNavigationBars() { mNavigationBarController.mNavigationBars = mock(SparseArray.class); - mDefaultNavBar = mock(NavigationBarFragment.class); + mDefaultNavBar = mock(NavigationBar.class); mDefaultNavBar.mDisplayId = DEFAULT_DISPLAY; doReturn(mDefaultNavBar) .when(mNavigationBarController.mNavigationBars).get(DEFAULT_DISPLAY); - mSecondaryNavBar = mock(NavigationBarFragment.class); + mSecondaryNavBar = mock(NavigationBar.class); mSecondaryNavBar.mDisplayId = SECONDARY_DISPLAY; doReturn(mSecondaryNavBar) .when(mNavigationBarController.mNavigationBars).get(SECONDARY_DISPLAY); @@ -90,22 +130,22 @@ public class NavigationBarControllerTest extends SysuiTestCase { @Test public void testCreateNavigationBarsIncludeDefaultTrue() { - doNothing().when(mNavigationBarController).createNavigationBar(any(), any()); + doNothing().when(mNavigationBarController).createNavigationBar(any(), any(), any()); mNavigationBarController.createNavigationBars(true, null); verify(mNavigationBarController).createNavigationBar( - argThat(display -> display.getDisplayId() == DEFAULT_DISPLAY), any()); + argThat(display -> display.getDisplayId() == DEFAULT_DISPLAY), any(), any()); } @Test public void testCreateNavigationBarsIncludeDefaultFalse() { - doNothing().when(mNavigationBarController).createNavigationBar(any(), any()); + doNothing().when(mNavigationBarController).createNavigationBar(any(), any(), any()); mNavigationBarController.createNavigationBars(false, null); verify(mNavigationBarController, never()).createNavigationBar( - argThat(display -> display.getDisplayId() == DEFAULT_DISPLAY), any()); + argThat(display -> display.getDisplayId() == DEFAULT_DISPLAY), any(), any()); } // Tests if NPE occurs when call checkNavBarModes() with invalid display. diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java index 80e33fbc6acb..7369c82e23ba 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.doNothing; @@ -32,6 +32,9 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.assist.AssistManager; +import com.android.systemui.navigationbar.buttons.ButtonDispatcher; +import com.android.systemui.navigationbar.NavigationBarInflaterView; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.recents.OverviewProxyService; import org.junit.After; @@ -54,6 +57,7 @@ public class NavigationBarInflaterViewTest extends SysuiTestCase { mDependency.injectMockDependency(AssistManager.class); mDependency.injectMockDependency(OverviewProxyService.class); mDependency.injectMockDependency(NavigationModeController.class); + mDependency.injectMockDependency(NavigationBarController.class); mNavBarInflaterView = spy(new NavigationBarInflaterView(mContext, null)); doNothing().when(mNavBarInflaterView).createInflaters(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java index f66524114292..51cf5016e8cc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -30,7 +30,9 @@ import androidx.test.runner.AndroidJUnit4; import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestableContext; -import com.android.systemui.statusbar.policy.KeyButtonDrawable; +import com.android.systemui.navigationbar.buttons.KeyButtonDrawable; +import com.android.systemui.navigationbar.RotationButton; +import com.android.systemui.navigationbar.RotationButtonController; import com.android.systemui.statusbar.policy.RotationLockController; import org.junit.Before; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java index 00cbddc87726..a643c2dc0f19 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -1,18 +1,20 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2020 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 + * 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. + * 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 com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar; import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; @@ -21,7 +23,7 @@ import static android.inputmethodservice.InputMethodService.IME_VISIBLE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; -import static com.android.systemui.statusbar.phone.NavigationBarFragment.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS; +import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -29,42 +31,35 @@ import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.annotation.LayoutRes; -import android.annotation.Nullable; -import android.app.Fragment; -import android.app.FragmentController; -import android.app.FragmentHostCallback; import android.content.BroadcastReceiver; import android.content.Context; import android.content.IntentFilter; import android.hardware.display.DisplayManagerGlobal; -import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; import android.testing.AndroidTestingRunner; -import android.testing.LeakCheck.Tracker; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.Display; import android.view.DisplayInfo; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; import android.view.WindowManager; -import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; +import android.view.WindowMetrics; +import android.view.accessibility.AccessibilityManager; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.systemui.Dependency; -import com.android.systemui.SysuiBaseFragmentTest; +import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestableContext; import com.android.systemui.accessibility.SystemActions; import com.android.systemui.assist.AssistManager; @@ -76,10 +71,15 @@ import com.android.systemui.recents.Recents; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.utils.leaks.LeakCheckedTest; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -90,94 +90,62 @@ import java.util.Optional; @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) @SmallTest -public class NavigationBarFragmentTest extends SysuiBaseFragmentTest { +public class NavigationBarTest extends SysuiTestCase { private static final int EXTERNAL_DISPLAY_ID = 2; - private static final int NAV_BAR_VIEW_ID = 43; - private Fragment mFragmentExternalDisplay; - private FragmentController mControllerExternalDisplay; + private NavigationBar mNavigationBar; + private NavigationBar mExternalDisplayNavigationBar; private SysuiTestableContext mSysuiTestableContextExternal; private OverviewProxyService mOverviewProxyService; private CommandQueue mCommandQueue; private SysUiState mMockSysUiState; + private Handler mHandler; @Mock private BroadcastDispatcher mBroadcastDispatcher; @Mock - private Divider mDivider; - @Mock - private Recents mRecents; - @Mock - private SystemActions mSystemActions; - @Mock private UiEventLogger mUiEventLogger; + @Rule + public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck(); private AccessibilityManagerWrapper mAccessibilityWrapper = - new AccessibilityManagerWrapper(mContext) { - Tracker mTracker = mLeakCheck.getTracker("accessibility_manager"); - - @Override - public void addCallback(AccessibilityServicesStateChangeListener listener) { - mTracker.getLeakInfo(listener).addAllocation(new Throwable()); - } - - @Override - public void removeCallback(AccessibilityServicesStateChangeListener listener) { - mTracker.getLeakInfo(listener).clearAllocations(); - } - }; - - public NavigationBarFragmentTest() { - super(NavigationBarFragment.class); - } - - protected void createRootView() { - mView = new NavigationBarFrame(mSysuiContext); - mView.setId(NAV_BAR_VIEW_ID); - } + new AccessibilityManagerWrapper(mContext); @Before - public void setupFragment() throws Exception { + public void setup() throws Exception { MockitoAnnotations.initMocks(this); mCommandQueue = new CommandQueue(mContext); setupSysuiDependency(); - createRootView(); - mOverviewProxyService = - mDependency.injectMockDependency(OverviewProxyService.class); + mDependency.injectMockDependency(AssistManager.class); + mDependency.injectMockDependency(KeyguardStateController.class); + mDependency.injectMockDependency(StatusBarStateController.class); + mDependency.injectMockDependency(NavigationBarController.class); + mDependency.injectMockDependency(Divider.class); + mOverviewProxyService = mDependency.injectMockDependency(OverviewProxyService.class); TestableLooper.get(this).runWithLooper(() -> { mHandler = new Handler(); - - mFragment = instantiate(mSysuiContext, NavigationBarFragment.class.getName(), null); - mFragments = FragmentController.createController( - new HostCallbacksForExternalDisplay(mSysuiContext)); - mFragments.attachHost(null); - mFragments.getFragmentManager().beginTransaction() - .replace(NAV_BAR_VIEW_ID, mFragment) - .commit(); - mControllerExternalDisplay = FragmentController.createController( - new HostCallbacksForExternalDisplay(mSysuiTestableContextExternal)); - mControllerExternalDisplay.attachHost(null); - mFragmentExternalDisplay = instantiate(mSysuiTestableContextExternal, - NavigationBarFragment.class.getName(), null); - mControllerExternalDisplay.getFragmentManager().beginTransaction() - .replace(NAV_BAR_VIEW_ID, mFragmentExternalDisplay) - .commit(); + mNavigationBar = createNavBar(mContext); + mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal); }); } private void setupSysuiDependency() { Display display = new Display(DisplayManagerGlobal.getInstance(), EXTERNAL_DISPLAY_ID, new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS); - mSysuiTestableContextExternal = (SysuiTestableContext) mSysuiContext.createDisplayContext( + mSysuiTestableContextExternal = (SysuiTestableContext) getContext().createDisplayContext( display); - injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); WindowManager windowManager = mock(WindowManager.class); Display defaultDisplay = mContext.getSystemService(WindowManager.class).getDefaultDisplay(); when(windowManager.getDefaultDisplay()).thenReturn( defaultDisplay); + WindowMetrics metrics = mContext.getSystemService(WindowManager.class) + .getMaximumWindowMetrics(); + when(windowManager.getMaximumWindowMetrics()).thenReturn(metrics); + doNothing().when(windowManager).addView(any(), any()); mContext.addMockSystemService(Context.WINDOW_SERVICE, windowManager); + mSysuiTestableContextExternal.addMockSystemService(Context.WINDOW_SERVICE, windowManager); mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper()); mDependency.injectTestDependency(AccessibilityManagerWrapper.class, mAccessibilityWrapper); @@ -188,20 +156,15 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest { @Test public void testHomeLongPress() { - NavigationBarFragment navigationBarFragment = (NavigationBarFragment) mFragment; - - mFragments.dispatchResume(); - processAllMessages(); - navigationBarFragment.onHomeLongClick(navigationBarFragment.getView()); + mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null)); + mNavigationBar.onHomeLongClick(mNavigationBar.getView()); verify(mUiEventLogger, times(1)).log(NAVBAR_ASSIST_LONGPRESS); } @Test public void testRegisteredWithDispatcher() { - mFragments.dispatchResume(); - processAllMessages(); - + mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null)); verify(mBroadcastDispatcher).registerReceiverWithHandler( any(BroadcastReceiver.class), any(IntentFilter.class), @@ -212,16 +175,12 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest { @Test public void testSetImeWindowStatusWhenImeSwitchOnDisplay() { // Create default & external NavBar fragment. - NavigationBarFragment defaultNavBar = (NavigationBarFragment) mFragment; - NavigationBarFragment externalNavBar = (NavigationBarFragment) mFragmentExternalDisplay; - mFragments.dispatchCreate(); - processAllMessages(); - mFragments.dispatchResume(); - processAllMessages(); - mControllerExternalDisplay.dispatchCreate(); - processAllMessages(); - mControllerExternalDisplay.dispatchResume(); - processAllMessages(); + NavigationBar defaultNavBar = mNavigationBar; + NavigationBar externalNavBar = mExternalDisplayNavigationBar; + doNothing().when(defaultNavBar).checkNavBarModes(); + doNothing().when(externalNavBar).checkNavBarModes(); + defaultNavBar.createView(null); + externalNavBar.createView(null); // Set IME window status for default NavBar. mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE, @@ -246,91 +205,36 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest { assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0); } - @Override - protected Fragment instantiate(Context context, String className, Bundle arguments) { + private NavigationBar createNavBar(Context context) { DeviceProvisionedController deviceProvisionedController = mock(DeviceProvisionedController.class); when(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true); assertNotNull(mAccessibilityWrapper); - return new NavigationBarFragment( + return spy(new NavigationBar(context, + mock(WindowManager.class), + () -> mock(AssistManager.class), + mock(AccessibilityManager.class), context.getDisplayId() == DEFAULT_DISPLAY ? mAccessibilityWrapper : mock(AccessibilityManagerWrapper.class), deviceProvisionedController, new MetricsLogger(), - mock(AssistManager.class), mOverviewProxyService, mock(NavigationModeController.class), mock(StatusBarStateController.class), mMockSysUiState, mBroadcastDispatcher, mCommandQueue, - mDivider, - Optional.of(mRecents), + mock(Divider.class), + Optional.of(mock(Recents.class)), () -> mock(StatusBar.class), mock(ShadeController.class), mock(NotificationRemoteInputManager.class), mock(SystemActions.class), mHandler, - mUiEventLogger); + mUiEventLogger)); } - private class HostCallbacksForExternalDisplay extends - FragmentHostCallback<NavigationBarFragmentTest> { - private Context mDisplayContext; - - HostCallbacksForExternalDisplay(Context context) { - super(context, mHandler, 0); - mDisplayContext = context; - } - - @Override - public NavigationBarFragmentTest onGetHost() { - return NavigationBarFragmentTest.this; - } - - @Override - public Fragment instantiate(Context context, String className, Bundle arguments) { - return NavigationBarFragmentTest.this.instantiate(context, className, arguments); - } - - @Override - public View onFindViewById(int id) { - return mView.findViewById(id); - } - - @Override - public LayoutInflater onGetLayoutInflater() { - return new LayoutInflaterWrapper(mDisplayContext); - } - } - - private static class LayoutInflaterWrapper extends LayoutInflater { - protected LayoutInflaterWrapper(Context context) { - super(context); - } - - @Override - public LayoutInflater cloneInContext(Context newContext) { - return null; - } - - @Override - public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, - boolean attachToRoot) { - NavigationBarView view = mock(NavigationBarView.class); - when(view.getDisplay()).thenReturn(mContext.getDisplay()); - when(view.getBackButton()).thenReturn(mock(ButtonDispatcher.class)); - when(view.getHomeButton()).thenReturn(mock(ButtonDispatcher.class)); - when(view.getRecentsButton()).thenReturn(mock(ButtonDispatcher.class)); - when(view.getAccessibilityButton()).thenReturn(mock(ButtonDispatcher.class)); - when(view.getRotateSuggestionButton()).thenReturn(mock(RotationContextButton.class)); - when(view.getBarTransitions()).thenReturn(mock(NavigationBarTransitions.class)); - when(view.getLightTransitionsController()).thenReturn( - mock(LightBarTransitionsController.class)); - when(view.getRotationButtonController()).thenReturn( - mock(RotationButtonController.class)); - when(view.isRecentsButtonVisible()).thenReturn(true); - return view; - } + private void processAllMessages() { + TestableLooper.get(this).processAllMessages(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java index 14c6e9f9624d..7e0920cc998d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java @@ -1,18 +1,20 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2020 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 + * 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. + * 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 com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -30,9 +32,13 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.assist.AssistManager; +import com.android.systemui.navigationbar.NavigationBarTransitions; +import com.android.systemui.navigationbar.NavigationBarView; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.phone.BarTransitions; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; @@ -53,6 +59,7 @@ public class NavigationBarTransitionsTest extends SysuiTestCase { mDependency.injectMockDependency(OverviewProxyService.class); mDependency.injectMockDependency(StatusBarStateController.class); mDependency.injectMockDependency(KeyguardStateController.class); + mDependency.injectMockDependency(NavigationBarController.class); doReturn(mContext) .when(mDependency.injectMockDependency(NavigationModeController.class)) .getCurrentUserContext(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java index d52d6860bf42..3f10c8da576b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java @@ -1,18 +1,20 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2020 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 + * 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. + * 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 com.android.systemui.statusbar.policy; +package com.android.systemui.navigationbar.buttons; import static android.view.KeyEvent.ACTION_DOWN; import static android.view.KeyEvent.ACTION_UP; @@ -23,12 +25,12 @@ import static android.view.KeyEvent.KEYCODE_APP_SWITCH; import static android.view.KeyEvent.KEYCODE_BACK; import static android.view.KeyEvent.KEYCODE_HOME; -import static com.android.systemui.statusbar.policy.KeyButtonView.NavBarButtonEvent.NAVBAR_BACK_BUTTON_LONGPRESS; -import static com.android.systemui.statusbar.policy.KeyButtonView.NavBarButtonEvent.NAVBAR_BACK_BUTTON_TAP; -import static com.android.systemui.statusbar.policy.KeyButtonView.NavBarButtonEvent.NAVBAR_HOME_BUTTON_LONGPRESS; -import static com.android.systemui.statusbar.policy.KeyButtonView.NavBarButtonEvent.NAVBAR_HOME_BUTTON_TAP; -import static com.android.systemui.statusbar.policy.KeyButtonView.NavBarButtonEvent.NAVBAR_OVERVIEW_BUTTON_LONGPRESS; -import static com.android.systemui.statusbar.policy.KeyButtonView.NavBarButtonEvent.NAVBAR_OVERVIEW_BUTTON_TAP; +import static com.android.systemui.navigationbar.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_BACK_BUTTON_LONGPRESS; +import static com.android.systemui.navigationbar.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_BACK_BUTTON_TAP; +import static com.android.systemui.navigationbar.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_HOME_BUTTON_LONGPRESS; +import static com.android.systemui.navigationbar.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_HOME_BUTTON_TAP; +import static com.android.systemui.navigationbar.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_OVERVIEW_BUTTON_LONGPRESS; +import static com.android.systemui.navigationbar.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_OVERVIEW_BUTTON_TAP; import static junit.framework.Assert.assertEquals; @@ -52,6 +54,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.systemui.SysuiTestCase; import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.navigationbar.buttons.KeyButtonView; import com.android.systemui.recents.OverviewProxyService; import org.junit.Before; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NavigationBarContextTest.java index 1fb28f0878bd..d56aa777706b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NavigationBarContextTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 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. @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar.buttons; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -35,7 +35,9 @@ import androidx.test.runner.AndroidJUnit4; import com.android.systemui.SysuiTestCase; import com.android.systemui.assist.AssistManager; -import com.android.systemui.statusbar.policy.KeyButtonDrawable; +import com.android.systemui.navigationbar.buttons.ContextualButton; +import com.android.systemui.navigationbar.buttons.ContextualButtonGroup; +import com.android.systemui.navigationbar.buttons.KeyButtonDrawable; import org.junit.Before; import org.junit.Ignore; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java index a04bcc0b29c5..0320103ceaa8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java @@ -1,18 +1,20 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2020 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 + * 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. + * 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 com.android.systemui.statusbar.phone; +package com.android.systemui.navigationbar.buttons; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -31,6 +33,7 @@ import android.view.View; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.navigationbar.buttons.NearestTouchFrame; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java index 73164b520b9d..7fabf8258198 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java @@ -54,8 +54,7 @@ public class OneHandedAnimationControllerTest extends OneHandedTestCase { MockitoAnnotations.initMocks(this); mTutorialHandler = new OneHandedTutorialHandler(mContext); - mOneHandedAnimationController = new OneHandedAnimationController( - new OneHandedSurfaceTransactionHelper(mContext)); + mOneHandedAnimationController = new OneHandedAnimationController(mContext); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java index f4c07004f632..95a230f6511c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java @@ -31,7 +31,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.model.SysUiState; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.phone.NavigationModeController; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.wm.shell.common.DisplayController; import org.junit.Before; @@ -79,7 +79,7 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase { @Test public void testOneHandedManager_registerForDisplayAreaOrganizer() { - verify(mMockDisplayAreaOrganizer, times(1)).registerTransitionCallback(mGestureHandler); + verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mGestureHandler); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java index 763f6e4fe94b..d7dba5fdf8c8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java @@ -87,10 +87,8 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { @Test public void testDefaultShouldNotInOneHanded() { - final OneHandedSurfaceTransactionHelper transactionHelper = - new OneHandedSurfaceTransactionHelper(mContext); final OneHandedAnimationController animationController = new OneHandedAnimationController( - transactionHelper); + mContext); OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer( mContext, mMockDisplayController, animationController, mMockTutorialHandler); diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java index f81d047b0f0c..990eb634e46f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java @@ -40,14 +40,12 @@ import org.junit.runner.RunWith; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper public class OneHandedSettingsUtilTest extends OneHandedTestCase { - OneHandedSettingsUtil mOneHandedSettingsUtil; ContentResolver mContentResolver; ContentObserver mContentObserver; boolean mOnChanged; @Before public void setUp() { - mOneHandedSettingsUtil = new OneHandedSettingsUtil(); mContentResolver = mContext.getContentResolver(); mContentObserver = new ContentObserver(mContext.getMainThreadHandler()) { @Override @@ -60,20 +58,20 @@ public class OneHandedSettingsUtilTest extends OneHandedTestCase { @Test public void testRegisterSecureKeyObserver() { - final Uri result = mOneHandedSettingsUtil.registerSettingsKeyObserver( + final Uri result = OneHandedSettingsUtil.registerSettingsKeyObserver( Settings.Secure.ONE_HANDED_MODE_ENABLED, mContentResolver, mContentObserver); assertThat(result).isNotNull(); - mOneHandedSettingsUtil.registerSettingsKeyObserver( + OneHandedSettingsUtil.registerSettingsKeyObserver( Settings.Secure.ONE_HANDED_MODE_ENABLED, mContentResolver, mContentObserver); } @Test public void testUnregisterSecureKeyObserver() { - mOneHandedSettingsUtil.registerSettingsKeyObserver( + OneHandedSettingsUtil.registerSettingsKeyObserver( Settings.Secure.ONE_HANDED_MODE_ENABLED, mContentResolver, mContentObserver); - mOneHandedSettingsUtil.unregisterSettingsKeyObserver(mContentResolver, mContentObserver); + OneHandedSettingsUtil.unregisterSettingsKeyObserver(mContentResolver, mContentObserver); assertThat(mOnChanged).isFalse(); @@ -85,19 +83,19 @@ public class OneHandedSettingsUtilTest extends OneHandedTestCase { @Test public void testGetSettingsIsOneHandedModeEnabled() { - assertThat(mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled( + assertThat(OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( mContentResolver)).isAnyOf(true, false); } @Test public void testGetSettingsTapsAppToExit() { - assertThat(mOneHandedSettingsUtil.getSettingsTapsAppToExit( + assertThat(OneHandedSettingsUtil.getSettingsTapsAppToExit( mContentResolver)).isAnyOf(true, false); } @Test public void testGetSettingsOneHandedModeTimeout() { - assertThat(mOneHandedSettingsUtil.getSettingsOneHandedModeTimeout( + assertThat(OneHandedSettingsUtil.getSettingsOneHandedModeTimeout( mContentResolver)).isAnyOf( ONE_HANDED_TIMEOUT_NEVER, ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS, @@ -107,7 +105,7 @@ public class OneHandedSettingsUtilTest extends OneHandedTestCase { @Test public void testGetSettingsSwipeToNotificationEnabled() { - assertThat(mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + assertThat(OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( mContentResolver)).isAnyOf(true, false); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java index 15881a2e7c18..8ae632dd5a47 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java @@ -29,7 +29,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.model.SysUiState; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.phone.NavigationModeController; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.wm.shell.common.DisplayController; import org.junit.Before; @@ -77,8 +77,7 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase { @Test public void testOneHandedManager_registerForDisplayAreaOrganizer() { - verify(mMockDisplayAreaOrganizer, times(1)) - .registerTransitionCallback(mTouchHandler); + verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mTouchHandler); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java index f2b77a0a936b..c75a8d2f5454 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java @@ -16,7 +16,6 @@ package com.android.systemui.onehanded; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.testing.AndroidTestingRunner; @@ -26,7 +25,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.model.SysUiState; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.phone.NavigationModeController; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.wm.shell.common.DisplayController; import org.junit.Before; @@ -75,7 +74,6 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { @Test public void testOneHandedManager_registerForDisplayAreaOrganizer() { - verify(mMockDisplayAreaOrganizer, times(1)) - .registerTransitionCallback(mTutorialHandler); + verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mTutorialHandler); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java index 6db2679ea116..f0e713e42046 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java @@ -16,8 +16,7 @@ package com.android.systemui.onehanded; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.times; +import static org.junit.Assume.assumeTrue; import static org.mockito.Mockito.verify; import android.os.SystemProperties; @@ -44,7 +43,6 @@ import org.mockito.MockitoAnnotations; public class OneHandedUITest extends OneHandedTestCase { private static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode"; - boolean mIsSupportOneHandedMode; CommandQueue mCommandQueue; KeyguardUpdateMonitor mKeyguardUpdateMonitor; OneHandedUI mOneHandedUI; @@ -52,110 +50,58 @@ public class OneHandedUITest extends OneHandedTestCase { @Mock OneHandedManagerImpl mMockOneHandedManagerImpl; @Mock - OneHandedSettingsUtil mMockSettingsUtil; - @Mock OneHandedTimeoutHandler mMockTimeoutHandler; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mIsSupportOneHandedMode = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false); mCommandQueue = new CommandQueue(mContext); mScreenLifecycle = new ScreenLifecycle(); mOneHandedUI = new OneHandedUI(mContext, mCommandQueue, mMockOneHandedManagerImpl, - mMockSettingsUtil, mScreenLifecycle); mOneHandedUI.start(); mKeyguardUpdateMonitor = mDependency.injectMockDependency(KeyguardUpdateMonitor.class); } + @Before + public void assumeOneHandedModeSupported() { + assumeTrue(SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)); + } + @Test public void testStartOneHanded() { - // Bypass test if device not support one-handed mode - if (!mIsSupportOneHandedMode) { - return; - } mOneHandedUI.startOneHanded(); - verify(mMockOneHandedManagerImpl, times(1)).startOneHanded(); + verify(mMockOneHandedManagerImpl).startOneHanded(); } @Test public void testStopOneHanded() { - // Bypass test if device not support one-handed mode - if (!mIsSupportOneHandedMode) { - return; - } mOneHandedUI.stopOneHanded(); - verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded(); - } - - @Test - public void testRegisterSettingsObserver_forEnabled() { - // Bypass test if device not support one-handed mode - if (!mIsSupportOneHandedMode) { - return; - } - final String key = Settings.Secure.ONE_HANDED_MODE_ENABLED; - - verify(mMockSettingsUtil, times(1)).registerSettingsKeyObserver(key, any(), any()); - } - - @Test - public void testRegisterSettingsObserver_forTimeout() { - // Bypass test if device not support one-handed mode - if (!mIsSupportOneHandedMode) { - return; - } - final String key = Settings.Secure.ONE_HANDED_MODE_TIMEOUT; - - verify(mMockSettingsUtil, times(1)).registerSettingsKeyObserver(key, any(), any()); - } - - @Test - public void testRegisterSettingsObserver_forTapAppExit() { - // Bypass test if device not support one-handed mode - if (!mIsSupportOneHandedMode) { - return; - } - final String key = Settings.Secure.TAPS_APP_TO_EXIT; - - verify(mMockSettingsUtil, times(1)).registerSettingsKeyObserver(key, any(), any()); + verify(mMockOneHandedManagerImpl).stopOneHanded(); } @Test public void tesSettingsObserver_updateTapAppToExit() { - // Bypass test if device not support one-handed mode - if (!mIsSupportOneHandedMode) { - return; - } Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.TAPS_APP_TO_EXIT, 1); - verify(mMockOneHandedManagerImpl, times(1)).setTaskChangeToExit(true); + verify(mMockOneHandedManagerImpl).setTaskChangeToExit(true); } @Test public void tesSettingsObserver_updateEnabled() { - // Bypass test if device not support one-handed mode - if (!mIsSupportOneHandedMode) { - return; - } Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.ONE_HANDED_MODE_ENABLED, 1); - verify(mMockOneHandedManagerImpl, times(1)).setOneHandedEnabled(true); + verify(mMockOneHandedManagerImpl).setOneHandedEnabled(true); } @Test public void tesSettingsObserver_updateTimeout() { - // Bypass test if device not support one-handed mode - if (!mIsSupportOneHandedMode) { - return; - } Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.ONE_HANDED_MODE_TIMEOUT, OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); @@ -166,10 +112,6 @@ public class OneHandedUITest extends OneHandedTestCase { @Test public void tesSettingsObserver_updateSwipeToNotification() { - // Bypass test if device not support one-handed mode - if (!mIsSupportOneHandedMode) { - return; - } Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1); @@ -179,24 +121,16 @@ public class OneHandedUITest extends OneHandedTestCase { @Ignore("Clarifying do not receive callback") @Test public void testKeyguardBouncerShowing_shouldStopOneHanded() { - // Bypass test if device not support one-handed mode - if (!mIsSupportOneHandedMode) { - return; - } mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true); - verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded(); + verify(mMockOneHandedManagerImpl).stopOneHanded(); } @Test public void testScreenTurningOff_shouldStopOneHanded() { - // Bypass test if device not support one-handed mode - if (!mIsSupportOneHandedMode) { - return; - } mScreenLifecycle.dispatchScreenTurningOff(); - verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded(); + verify(mMockOneHandedManagerImpl).stopOneHanded(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index d338cbf51fb5..b46c6ef81fa7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -150,7 +150,10 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { return new QSFragment( new RemoteInputQuickSettingsDisabler(context, mock(ConfigurationController.class), commandQueue), - new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()), + new InjectionInflationController( + SystemUIFactory.getInstance() + .getSysUIComponent() + .createViewInstanceCreatorFactory()), mock(QSTileHost.class), mock(StatusBarStateController.class), commandQueue, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index bdb7166f5db1..c8e1a74d969f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -78,7 +78,7 @@ import javax.inject.Provider; @RunWith(AndroidTestingRunner.class) @SmallTest -@RunWithLooper +@RunWithLooper(setAsMainLooper = true) public class QSTileHostTest extends SysuiTestCase { private static String MOCK_STATE_STRING = "MockState"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt index c2579dd46e78..3aa40dec1fad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt @@ -53,7 +53,7 @@ import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) -@TestableLooper.RunWithLooper +@TestableLooper.RunWithLooper(setAsMainLooper = true) class CustomTileTest : SysuiTestCase() { companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java index cccb65d11228..61a0d6c17eed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java @@ -244,6 +244,8 @@ public class QSTileImplTest extends SysuiTestCase { assertNotEquals(DESTROYED, mTile.getLifecycle().getCurrentState()); mTile.handleDestroy(); + mTestableLooper.processAllMessages(); + assertEquals(DESTROYED, mTile.getLifecycle().getCurrentState()); } @@ -298,6 +300,25 @@ public class QSTileImplTest extends SysuiTestCase { assertNotEquals(DESTROYED, mTile.getLifecycle().getCurrentState()); } + @Test + public void testRefreshStateAfterDestroyedDoesNotCrash() { + mTile.destroy(); + mTile.refreshState(); + + mTestableLooper.processAllMessages(); + } + + @Test + public void testSetListeningAfterDestroyedDoesNotCrash() { + Object o = new Object(); + mTile.destroy(); + + mTile.setListening(o, true); + mTile.setListening(o, false); + + mTestableLooper.processAllMessages(); + } + private void assertEvent(UiEventLogger.UiEventEnum eventType, UiEventLoggerFake.FakeUiEvent fakeEvent) { assertEquals(eventType.getId(), fakeEvent.eventId); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt index f70106a64968..2006a75c0e16 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt @@ -38,7 +38,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) -@RunWithLooper +@RunWithLooper(setAsMainLooper = true) @SmallTest class BatterySaverTileTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java index 8ece62281f77..5d14898cdd2c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java @@ -58,7 +58,7 @@ import java.util.List; @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper +@TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class CastTileTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java index 184329ec6e5f..e23f92616565 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java @@ -86,7 +86,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { CompletableFuture<List<Notification.Action>> smartActionsFuture = mScreenshotSmartActions.getSmartActionsFuture( "", Uri.parse("content://authority/data"), bitmap, smartActionsProvider, - true, UserHandle.getUserHandleForUid(UserHandle.myUserId())); + true, UserHandle.of(UserHandle.myUserId())); assertNotNull(smartActionsFuture); List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS); assertEquals(Collections.emptyList(), smartActions); @@ -126,7 +126,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { CompletableFuture<List<Notification.Action>> smartActionsFuture = mScreenshotSmartActions.getSmartActionsFuture( "", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider, - true, UserHandle.getUserHandleForUid(UserHandle.myUserId())); + true, UserHandle.of(UserHandle.myUserId())); verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(), any()); assertNotNull(smartActionsFuture); List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS); @@ -140,7 +140,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE); mScreenshotSmartActions.getSmartActionsFuture( "", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider, true, - UserHandle.getUserHandleForUid(UserHandle.myUserId())); + UserHandle.of(UserHandle.myUserId())); verify(mSmartActionsProvider, times(1)).getActions(any(), any(), any(), any(), any()); } @@ -156,7 +156,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { CompletableFuture<List<Notification.Action>> smartActionsFuture = mScreenshotSmartActions.getSmartActionsFuture("", null, bitmap, actionsProvider, - true, UserHandle.getUserHandleForUid(UserHandle.myUserId())); + true, UserHandle.of(UserHandle.myUserId())); assertNotNull(smartActionsFuture); List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS); assertEquals(smartActions.size(), 0); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java index 644ed3d6e2b5..8089561f44a0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java @@ -34,7 +34,6 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.HeadsUpManager; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt index 56df1939be56..a36a4c43e278 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt @@ -29,7 +29,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.notification.ActivityLaunchAnimator import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.DozeParameters -import com.android.systemui.statusbar.phone.NotificationShadeWindowController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.mockito.eq import org.junit.Before diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index 6f46923cda5e..1259d28c3400 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -19,12 +19,10 @@ package com.android.systemui.statusbar; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -46,9 +44,9 @@ import com.android.systemui.statusbar.notification.DynamicChildBindController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -212,19 +210,6 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { } @Test - public void testUpdateNotificationViews_appOps() throws Exception { - NotificationEntry entry0 = createEntry(); - entry0.setRow(spy(entry0.getRow())); - when(mEntryManager.getVisibleNotifications()).thenReturn( - Lists.newArrayList(entry0)); - mListContainer.addContainerView(entry0.getRow()); - - mViewHierarchyManager.updateNotificationViews(); - - verify(entry0.getRow(), times(1)).showAppOpsIcons(any()); - } - - @Test public void testReentrantCallsToOnDynamicPrivacyChangedPostForLater() { // GIVEN a ListContainer that will make a re-entrant call to updateNotificationViews() mMadeReentrantCall = false; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java index ea1b498801ec..baeedcfc3fc5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java @@ -32,14 +32,17 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.NotificationPresenter; +import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -54,109 +57,126 @@ public class VisualStabilityManagerTest extends SysuiTestCase { private ExpandableNotificationRow mRow = mock(ExpandableNotificationRow.class); private NotificationEntry mEntry; + private StatusBarStateController.StateListener mStatusBarStateListener; + private WakefulnessLifecycle.Observer mWakefulnessObserver; + @Before public void setUp() { + StatusBarStateController statusBarStateController = mock(StatusBarStateController.class); + WakefulnessLifecycle wakefulnessLifecycle = mock(WakefulnessLifecycle.class); + mTestableLooper = TestableLooper.get(this); mVisualStabilityManager = new VisualStabilityManager( mock(NotificationEntryManager.class), - new Handler(mTestableLooper.getLooper())); + new Handler(mTestableLooper.getLooper()), + statusBarStateController, + wakefulnessLifecycle); - mVisualStabilityManager.setUpWithPresenter(mock(NotificationPresenter.class)); mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider); mEntry = new NotificationEntryBuilder().build(); mEntry.setRow(mRow); when(mRow.getEntry()).thenReturn(mEntry); + + ArgumentCaptor<StatusBarStateController.StateListener> stateListenerCaptor = + ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); + verify(statusBarStateController).addCallback(stateListenerCaptor.capture()); + mStatusBarStateListener = stateListenerCaptor.getValue(); + + ArgumentCaptor<WakefulnessLifecycle.Observer> wakefulnessObserverCaptor = + ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class); + verify(wakefulnessLifecycle).addObserver(wakefulnessObserverCaptor.capture()); + mWakefulnessObserver = wakefulnessObserverCaptor.getValue(); } @Test public void testPanelExpansion() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); assertFalse(mVisualStabilityManager.canReorderNotification(mRow)); - mVisualStabilityManager.setPanelExpanded(false); + setPanelExpanded(false); assertTrue(mVisualStabilityManager.canReorderNotification(mRow)); } @Test public void testScreenOn() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); assertFalse(mVisualStabilityManager.canReorderNotification(mRow)); - mVisualStabilityManager.setScreenOn(false); + setScreenOn(false); assertTrue(mVisualStabilityManager.canReorderNotification(mRow)); } @Test public void testReorderingAllowedChangesScreenOn() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); assertFalse(mVisualStabilityManager.isReorderingAllowed()); - mVisualStabilityManager.setScreenOn(false); + setScreenOn(false); assertTrue(mVisualStabilityManager.isReorderingAllowed()); } @Test public void testReorderingAllowedChangesPanel() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); assertFalse(mVisualStabilityManager.isReorderingAllowed()); - mVisualStabilityManager.setPanelExpanded(false); + setPanelExpanded(false); assertTrue(mVisualStabilityManager.isReorderingAllowed()); } @Test public void testCallBackCalledScreenOn() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */); - mVisualStabilityManager.setScreenOn(false); + setScreenOn(false); verify(mCallback).onChangeAllowed(); } @Test public void testCallBackCalledPanelExpanded() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */); - mVisualStabilityManager.setPanelExpanded(false); + setPanelExpanded(false); verify(mCallback).onChangeAllowed(); } @Test public void testCallBackExactlyOnce() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */); - mVisualStabilityManager.setScreenOn(false); - mVisualStabilityManager.setScreenOn(true); - mVisualStabilityManager.setScreenOn(false); + setScreenOn(false); + setScreenOn(true); + setScreenOn(false); verify(mCallback).onChangeAllowed(); } @Test public void testCallBackCalledContinuouslyWhenRequested() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback, true /* persistent */); - mVisualStabilityManager.setScreenOn(false); - mVisualStabilityManager.setScreenOn(true); - mVisualStabilityManager.setScreenOn(false); + setScreenOn(false); + setScreenOn(true); + setScreenOn(false); verify(mCallback, times(2)).onChangeAllowed(); } @Test public void testAddedCanReorder() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); mVisualStabilityManager.notifyViewAddition(mRow); assertTrue(mVisualStabilityManager.canReorderNotification(mRow)); } @Test public void testReorderingVisibleHeadsUpNotAllowed() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(true); mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true); assertFalse(mVisualStabilityManager.canReorderNotification(mRow)); @@ -164,8 +184,8 @@ public class VisualStabilityManagerTest extends SysuiTestCase { @Test public void testReorderingVisibleHeadsUpAllowed() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(false); mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true); assertTrue(mVisualStabilityManager.canReorderNotification(mRow)); @@ -173,8 +193,8 @@ public class VisualStabilityManagerTest extends SysuiTestCase { @Test public void testReorderingVisibleHeadsUpAllowedOnce() { - mVisualStabilityManager.setPanelExpanded(true); - mVisualStabilityManager.setScreenOn(true); + setPanelExpanded(true); + setScreenOn(true); when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(false); mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true); mVisualStabilityManager.onReorderingFinished(); @@ -183,33 +203,33 @@ public class VisualStabilityManagerTest extends SysuiTestCase { @Test public void testPulsing() { - mVisualStabilityManager.setPulsing(true); + setPulsing(true); assertFalse(mVisualStabilityManager.canReorderNotification(mRow)); - mVisualStabilityManager.setPulsing(false); + setPulsing(false); assertTrue(mVisualStabilityManager.canReorderNotification(mRow)); } @Test public void testReorderingAllowedChanges_Pulsing() { - mVisualStabilityManager.setPulsing(true); + setPulsing(true); assertFalse(mVisualStabilityManager.isReorderingAllowed()); - mVisualStabilityManager.setPulsing(false); + setPulsing(false); assertTrue(mVisualStabilityManager.isReorderingAllowed()); } @Test public void testCallBackCalled_Pulsing() { - mVisualStabilityManager.setPulsing(true); + setPulsing(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */); - mVisualStabilityManager.setPulsing(false); + setPulsing(false); verify(mCallback).onChangeAllowed(); } @Test public void testTemporarilyAllowReorderingNotifiesCallbacks() { // GIVEN having the panel open (which would block reordering) - mVisualStabilityManager.setScreenOn(true); - mVisualStabilityManager.setPanelExpanded(true); + setScreenOn(true); + setPanelExpanded(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */); // WHEN we temprarily allow reordering @@ -223,7 +243,7 @@ public class VisualStabilityManagerTest extends SysuiTestCase { @Test public void testTemporarilyAllowReorderingDoesntOverridePulsing() { // GIVEN we are in a pulsing state - mVisualStabilityManager.setPulsing(true); + setPulsing(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */); // WHEN we temprarily allow reordering @@ -237,8 +257,8 @@ public class VisualStabilityManagerTest extends SysuiTestCase { @Test public void testTemporarilyAllowReorderingExpires() { // GIVEN having the panel open (which would block reordering) - mVisualStabilityManager.setScreenOn(true); - mVisualStabilityManager.setPanelExpanded(true); + setScreenOn(true); + setPanelExpanded(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */); // WHEN we temprarily allow reordering and then wait until the window expires @@ -249,4 +269,20 @@ public class VisualStabilityManagerTest extends SysuiTestCase { // THEN reordering is no longer allowed assertFalse(mVisualStabilityManager.isReorderingAllowed()); } + + private void setPanelExpanded(boolean expanded) { + mStatusBarStateListener.onExpandedChanged(expanded); + } + + private void setPulsing(boolean pulsing) { + mStatusBarStateListener.onPulsingChanged(pulsing); + } + + private void setScreenOn(boolean screenOn) { + if (screenOn) { + mWakefulnessObserver.onStartedWakingUp(); + } else { + mWakefulnessObserver.onFinishedGoingToSleep(); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java index 960ea79f36b4..ce8ce2e39bcc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java @@ -77,8 +77,6 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { private NotificationEntryBuilder mEntryBuilder; private AppOpsCoordinator mAppOpsCoordinator; private NotifFilter mForegroundFilter; - private NotifCollectionListener mNotifCollectionListener; - private AppOpsController.Callback mAppOpsCallback; private NotifLifetimeExtender mForegroundNotifLifetimeExtender; private NotifSection mFgsSection; @@ -113,19 +111,6 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { lifetimeExtenderCaptor.capture()); mForegroundNotifLifetimeExtender = lifetimeExtenderCaptor.getValue(); - // capture notifCollectionListener - ArgumentCaptor<NotifCollectionListener> notifCollectionCaptor = - ArgumentCaptor.forClass(NotifCollectionListener.class); - verify(mNotifPipeline, times(1)).addCollectionListener( - notifCollectionCaptor.capture()); - mNotifCollectionListener = notifCollectionCaptor.getValue(); - - // capture app ops callback - ArgumentCaptor<AppOpsController.Callback> appOpsCaptor = - ArgumentCaptor.forClass(AppOpsController.Callback.class); - verify(mAppOpsController).addCallback(any(int[].class), appOpsCaptor.capture()); - mAppOpsCallback = appOpsCaptor.getValue(); - mFgsSection = mAppOpsCoordinator.getSection(); } @@ -230,136 +215,6 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { } @Test - public void testAppOpsUpdateOnlyAppliedToRelevantNotificationWithStandardLayout() { - // GIVEN three current notifications, two with the same key but from different users - NotificationEntry entry1 = new NotificationEntryBuilder() - .setUser(new UserHandle(NOTIF_USER_ID)) - .setPkg(TEST_PKG) - .setId(1) - .build(); - NotificationEntry entry2 = new NotificationEntryBuilder() - .setUser(new UserHandle(NOTIF_USER_ID)) - .setPkg(TEST_PKG) - .setId(2) - .build(); - NotificationEntry entry3_diffUser = new NotificationEntryBuilder() - .setUser(new UserHandle(NOTIF_USER_ID + 1)) - .setPkg(TEST_PKG) - .setId(2) - .build(); - when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry3_diffUser)); - - // GIVEN that only entry2 has a standard layout - when(mForegroundServiceController.getStandardLayoutKeys(NOTIF_USER_ID, TEST_PKG)) - .thenReturn(new ArraySet<>(List.of(entry2.getKey()))); - - // WHEN a new app ops code comes in - mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true); - mExecutor.runAllReady(); - - // THEN entry2's app ops are updated, but no one else's are - assertEquals( - new ArraySet<>(), - entry1.mActiveAppOps); - assertEquals( - new ArraySet<>(List.of(47)), - entry2.mActiveAppOps); - assertEquals( - new ArraySet<>(), - entry3_diffUser.mActiveAppOps); - } - - @Test - public void testAppOpsUpdateAppliedToAllNotificationsWithStandardLayouts() { - // GIVEN three notifications with standard layouts - NotificationEntry entry1 = new NotificationEntryBuilder() - .setUser(new UserHandle(NOTIF_USER_ID)) - .setPkg(TEST_PKG) - .setId(1) - .build(); - NotificationEntry entry2 = new NotificationEntryBuilder() - .setUser(new UserHandle(NOTIF_USER_ID)) - .setPkg(TEST_PKG) - .setId(2) - .build(); - NotificationEntry entry3 = new NotificationEntryBuilder() - .setUser(new UserHandle(NOTIF_USER_ID)) - .setPkg(TEST_PKG) - .setId(3) - .build(); - when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry3)); - when(mForegroundServiceController.getStandardLayoutKeys(NOTIF_USER_ID, TEST_PKG)) - .thenReturn(new ArraySet<>(List.of(entry1.getKey(), entry2.getKey(), - entry3.getKey()))); - - // WHEN a new app ops code comes in - mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true); - mExecutor.runAllReady(); - - // THEN all entries get updated - assertEquals( - new ArraySet<>(List.of(47)), - entry1.mActiveAppOps); - assertEquals( - new ArraySet<>(List.of(47)), - entry2.mActiveAppOps); - assertEquals( - new ArraySet<>(List.of(47)), - entry3.mActiveAppOps); - } - - @Test - public void testAppOpsAreRemoved() { - // GIVEN One notification which is associated with app ops - NotificationEntry entry = new NotificationEntryBuilder() - .setUser(new UserHandle(NOTIF_USER_ID)) - .setPkg(TEST_PKG) - .setId(2) - .build(); - when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry)); - when(mForegroundServiceController.getStandardLayoutKeys(0, TEST_PKG)) - .thenReturn(new ArraySet<>(List.of(entry.getKey()))); - - // GIVEN that the notification's app ops are already [47, 33] - mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true); - mAppOpsCallback.onActiveStateChanged(33, NOTIF_USER_ID, TEST_PKG, true); - mExecutor.runAllReady(); - assertEquals( - new ArraySet<>(List.of(47, 33)), - entry.mActiveAppOps); - - // WHEN one of the app ops is removed - mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, false); - mExecutor.runAllReady(); - - // THEN the entry's active app ops are updated as well - assertEquals( - new ArraySet<>(List.of(33)), - entry.mActiveAppOps); - } - - @Test - public void testNullAppOps() { - // GIVEN one notification with app ops - NotificationEntry entry = new NotificationEntryBuilder() - .setUser(new UserHandle(NOTIF_USER_ID)) - .setPkg(TEST_PKG) - .setId(2) - .build(); - entry.mActiveAppOps.clear(); - entry.mActiveAppOps.addAll(List.of(47, 33)); - - // WHEN the notification is updated and the foreground service controller returns null for - // this notification - when(mForegroundServiceController.getAppOps(entry.getSbn().getUser().getIdentifier(), - entry.getSbn().getPackageName())).thenReturn(null); - mNotifCollectionListener.onEntryUpdated(entry); - - // THEN the entry's active app ops is updated to empty - assertTrue(entry.mActiveAppOps.isEmpty()); - } - - @Test public void testIncludeFGSInSection_importanceDefault() { // GIVEN the notification represents a colorized foreground service with > min importance mEntryBuilder diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java new file mode 100644 index 000000000000..605b4d18d2f1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.statusbar.notification.collection.coordinator; + +import static junit.framework.Assert.assertFalse; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.notification.collection.NotifPipeline; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; +import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class VisualStabilityCoordinatorTest extends SysuiTestCase { + + private VisualStabilityCoordinator mCoordinator; + + // captured listeners and pluggables: + private NotifCollectionListener mCollectionListener; + + @Mock private NotifPipeline mNotifPipeline; + @Mock private WakefulnessLifecycle mWakefulnessLifecycle; + @Mock private StatusBarStateController mStatusBarStateController; + @Mock private Pluggable.PluggableListener<NotifStabilityManager> mInvalidateListener; + @Mock private HeadsUpManager mHeadsUpManager; + + @Captor private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessObserverCaptor; + @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mSBStateListenerCaptor; + @Captor private ArgumentCaptor<NotifStabilityManager> mNotifStabilityManagerCaptor; + @Captor private ArgumentCaptor<NotifCollectionListener> mNotifCollectionListenerCaptor; + + private FakeSystemClock mFakeSystemClock = new FakeSystemClock(); + private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock); + + private WakefulnessLifecycle.Observer mWakefulnessObserver; + private StatusBarStateController.StateListener mStatusBarStateListener; + private NotifStabilityManager mNotifStabilityManager; + private NotificationEntry mEntry; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mCoordinator = new VisualStabilityCoordinator( + mHeadsUpManager, + mWakefulnessLifecycle, + mStatusBarStateController, + mFakeExecutor); + + mCoordinator.attach(mNotifPipeline); + + // capture arguments: + verify(mWakefulnessLifecycle).addObserver(mWakefulnessObserverCaptor.capture()); + mWakefulnessObserver = mWakefulnessObserverCaptor.getValue(); + + verify(mStatusBarStateController).addCallback(mSBStateListenerCaptor.capture()); + mStatusBarStateListener = mSBStateListenerCaptor.getValue(); + + verify(mNotifPipeline).setVisualStabilityManager(mNotifStabilityManagerCaptor.capture()); + mNotifStabilityManager = mNotifStabilityManagerCaptor.getValue(); + mNotifStabilityManager.setInvalidationListener(mInvalidateListener); + + mEntry = new NotificationEntryBuilder() + .setPkg("testPkg1") + .build(); + + when(mHeadsUpManager.isAlerting(mEntry.getKey())).thenReturn(false); + } + + @Test + public void testScreenOff_groupAndSectionChangesAllowed() { + // GIVEN screen is off, panel isn't expanded and device isn't pulsing + setScreenOn(false); + setPanelExpanded(false); + setPulsing(false); + + // THEN group changes are allowed + assertTrue(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + + // THEN section changes are allowed + assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + } + + @Test + public void testPanelNotExpanded_groupAndSectionChangesAllowed() { + // GIVEN screen is on but the panel isn't expanded and device isn't pulsing + setScreenOn(true); + setPanelExpanded(false); + setPulsing(false); + + // THEN group changes are allowed + assertTrue(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + + // THEN section changes are allowed + assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + } + + @Test + public void testPanelExpanded_groupAndSectionChangesNotAllowed() { + // GIVEN the panel true expanded and device isn't pulsing + setScreenOn(true); + setPanelExpanded(true); + setPulsing(false); + + // THEN group changes are NOT allowed + assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + + // THEN section changes are NOT allowed + assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + } + + @Test + public void testPulsing_screenOff_groupAndSectionChangesNotAllowed() { + // GIVEN the device is pulsing and screen is off + setScreenOn(false); + setPulsing(true); + + // THEN group changes are NOT allowed + assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + + // THEN section changes are NOT allowed + assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + } + + @Test + public void testPulsing_panelNotExpanded_groupAndSectionChangesNotAllowed() { + // GIVEN the device is pulsing and screen is off with the panel not expanded + setScreenOn(false); + setPanelExpanded(false); + setPulsing(true); + + // THEN group changes are NOT allowed + assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + + // THEN section changes are NOT allowed + assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + } + + @Test + public void testOverrideReorderingSuppression_onlySectionChangesAllowed() { + // GIVEN section changes typically wouldn't be allowed because the panel is expanded and + // we're not pulsing + setScreenOn(true); + setPanelExpanded(true); + setPulsing(true); + + // WHEN we temporarily allow section changes for this notification entry + mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis()); + + // THEN group changes aren't allowed + assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + + // THEN section changes are allowed for this notification but not other notifications + assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + assertFalse(mNotifStabilityManager.isSectionChangeAllowed( + new NotificationEntryBuilder() + .setPkg("testPkg2") + .build())); + } + + @Test + public void testTemporarilyAllowSectionChanges_callsInvalidate() { + // GIVEN section changes typically wouldn't be allowed because the panel is expanded + setScreenOn(true); + setPanelExpanded(true); + setPulsing(false); + + // WHEN we temporarily allow section changes for this notification entry + mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.uptimeMillis()); + + // THEN the notification list is invalidated + verifyInvalidateCalled(true); + } + + @Test + public void testTemporarilyAllowSectionChanges_noInvalidationCalled() { + // GIVEN section changes typically WOULD be allowed + setScreenOn(false); + setPanelExpanded(false); + setPulsing(false); + + // WHEN we temporarily allow section changes for this notification entry + mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis()); + + // THEN invalidate is not called because this entry was never suppressed from reordering + verifyInvalidateCalled(false); + } + + @Test + public void testTemporarilyAllowSectionChangesTimeout() { + // GIVEN section changes typically WOULD be allowed + setScreenOn(false); + setPanelExpanded(false); + setPulsing(false); + assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + + // WHEN we temporarily allow section changes for this notification entry + mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis()); + + // THEN invalidate is not called because this entry was never suppressed from reordering; + // THEN section changes are allowed for this notification + verifyInvalidateCalled(false); + assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + + // WHEN we're pulsing (now disallowing reordering) + setPulsing(true); + + // THEN we're still allowed to reorder this section because it's still in the list of + // notifications to allow section changes + assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + + // WHEN the timeout for the temporarily allow section reordering runnable is finsihed + mFakeExecutor.advanceClockToNext(); + mFakeExecutor.runNextReady(); + + // THEN section changes aren't allowed anymore + assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + } + + @Test + public void testTemporarilyAllowSectionChanges_isPulsingChangeBeforeTimeout() { + // GIVEN section changes typically wouldn't be allowed because the device is pulsing + setScreenOn(false); + setPanelExpanded(false); + setPulsing(true); + + // WHEN we temporarily allow section changes for this notification entry + mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis()); + verifyInvalidateCalled(true); // can now reorder, so invalidates + + // WHEN reordering is now allowed because device isn't pulsing anymore + setPulsing(false); + + // THEN invalidate isn't called since reordering was already allowed + verifyInvalidateCalled(false); + } + + @Test + public void testNeverSuppressedChanges_noInvalidationCalled() { + // GIVEN no notifications are currently being suppressed from grouping nor being sorted + + // WHEN device isn't pulsing anymore + setPulsing(false); + + // WHEN screen isn't on + setScreenOn(false); + + // WHEN panel isn't expanded + setPanelExpanded(false); + + // THEN we never see any calls to invalidate since there weren't any notifications that + // were being suppressed from grouping or section changes + verifyInvalidateCalled(false); + } + + @Test + public void testHeadsUp_allowedToChangeGroupAndSection() { + // GIVEN group + section changes disallowed + setScreenOn(true); + setPanelExpanded(true); + setPulsing(true); + assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + + // GIVEN mEntry is a HUN + when(mHeadsUpManager.isAlerting(mEntry.getKey())).thenReturn(true); + + // THEN group + section changes are allowed + assertTrue(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + + } + + private void setPulsing(boolean pulsing) { + mStatusBarStateListener.onPulsingChanged(pulsing); + } + + private void setScreenOn(boolean screenOn) { + if (screenOn) { + mWakefulnessObserver.onStartedWakingUp(); + } else { + mWakefulnessObserver.onFinishedGoingToSleep(); + } + } + + private void setPanelExpanded(boolean expanded) { + mStatusBarStateListener.onExpandedChanged(expanded); + } + + private void verifyInvalidateCalled(boolean invalidateCalled) { + if (invalidateCalled) { + verify(mInvalidateListener).onPluggableInvalidated(mNotifStabilityManager); + } else { + verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager); + } + + reset(mInvalidateListener); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java new file mode 100644 index 000000000000..bbe92f67ca2e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.statusbar.notification.collection.render; + +import static org.junit.Assert.assertEquals; + +import android.content.Context; +import android.testing.AndroidTestingRunner; +import android.view.View; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.List; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class ShadeViewDifferTest extends SysuiTestCase { + private ShadeViewDiffer mDiffer; + + private FakeController mRootController = new FakeController(mContext, "RootController"); + private FakeController mController1 = new FakeController(mContext, "Controller1"); + private FakeController mController2 = new FakeController(mContext, "Controller2"); + private FakeController mController3 = new FakeController(mContext, "Controller3"); + private FakeController mController4 = new FakeController(mContext, "Controller4"); + private FakeController mController5 = new FakeController(mContext, "Controller5"); + private FakeController mController6 = new FakeController(mContext, "Controller6"); + private FakeController mController7 = new FakeController(mContext, "Controller7"); + + @Mock + ShadeViewDifferLogger mLogger; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mDiffer = new ShadeViewDiffer(mRootController, mLogger); + } + + @Test + public void testAddInitialViews() { + // WHEN a spec is applied to an empty root + // THEN the final tree matches the spec + applySpecAndCheck( + node(mController1), + node(mController2, + node(mController3), + node(mController4) + ), + node(mController5) + ); + } + + @Test + public void testDetachViews() { + // GIVEN a preexisting tree of controllers + applySpecAndCheck( + node(mController1), + node(mController2, + node(mController3), + node(mController4) + ), + node(mController5) + ); + + // WHEN the new spec removes nodes + // THEN the final tree matches the spec + applySpecAndCheck( + node(mController5) + ); + } + + @Test + public void testReparentChildren() { + // GIVEN a preexisting tree of controllers + applySpecAndCheck( + node(mController1), + node(mController2, + node(mController3), + node(mController4) + ), + node(mController5) + ); + + // WHEN the parents of the controllers are all shuffled around + // THEN the final tree matches the spec + applySpecAndCheck( + node(mController1), + node(mController4), + node(mController3, + node(mController2) + ) + ); + } + + @Test + public void testReorderChildren() { + // GIVEN a preexisting tree of controllers + applySpecAndCheck( + node(mController1), + node(mController2), + node(mController3), + node(mController4) + ); + + // WHEN the children change order + // THEN the final tree matches the spec + applySpecAndCheck( + node(mController3), + node(mController2), + node(mController4), + node(mController1) + ); + } + + @Test + public void testRemovedGroupsAreKeptTogether() { + // GIVEN a preexisting tree with a group + applySpecAndCheck( + node(mController1), + node(mController2, + node(mController3), + node(mController4), + node(mController5) + ) + ); + + // WHEN the new spec removes the entire group + applySpecAndCheck( + node(mController1) + ); + + // THEN the group children are still attached to their parent + assertEquals(mController2.getView(), mController3.getView().getParent()); + assertEquals(mController2.getView(), mController4.getView().getParent()); + assertEquals(mController2.getView(), mController5.getView().getParent()); + } + + @Test + public void testUnmanagedViews() { + // GIVEN a preexisting tree of controllers + applySpecAndCheck( + node(mController1), + node(mController2, + node(mController3), + node(mController4) + ), + node(mController5) + ); + + // GIVEN some additional unmanaged views attached to the tree + View unmanagedView1 = new View(mContext); + View unmanagedView2 = new View(mContext); + + mRootController.getView().addView(unmanagedView1, 1); + mController2.getView().addView(unmanagedView2, 0); + + // WHEN a new spec is applied with additional nodes + // THEN the final tree matches the spec + applySpecAndCheck( + node(mController1), + node(mController2, + node(mController3), + node(mController4), + node(mController6) + ), + node(mController5), + node(mController7) + ); + + // THEN the unmanaged views have been pushed to the end of their parents + assertEquals(unmanagedView1, mRootController.view.getChildAt(4)); + assertEquals(unmanagedView2, mController2.view.getChildAt(3)); + } + + private void applySpecAndCheck(NodeSpec spec) { + mDiffer.applySpec(spec); + checkMatchesSpec(spec); + } + + private void applySpecAndCheck(SpecBuilder... children) { + applySpecAndCheck(node(mRootController, children).build()); + } + + private void checkMatchesSpec(NodeSpec spec) { + final NodeController parent = spec.getController(); + final List<NodeSpec> children = spec.getChildren(); + + for (int i = 0; i < children.size(); i++) { + NodeSpec childSpec = children.get(i); + View view = parent.getChildAt(i); + + assertEquals( + "Child " + i + " of parent " + parent.getNodeLabel() + " should be " + + childSpec.getController().getNodeLabel() + " but is instead " + + (view != null ? mDiffer.getViewLabel(view) : "null"), + view, + childSpec.getController().getView()); + + if (!childSpec.getChildren().isEmpty()) { + checkMatchesSpec(childSpec); + } + } + } + + private static class FakeController implements NodeController { + + public final FrameLayout view; + private final String mLabel; + + FakeController(Context context, String label) { + view = new FrameLayout(context); + mLabel = label; + } + + @NonNull + @Override + public String getNodeLabel() { + return mLabel; + } + + @NonNull + @Override + public FrameLayout getView() { + return view; + } + + @Override + public int getChildCount() { + return view.getChildCount(); + } + + @Override + public View getChildAt(int index) { + return view.getChildAt(index); + } + + @Override + public void addChildAt(@NonNull NodeController child, int index) { + view.addView(child.getView(), index); + } + + @Override + public void moveChildTo(@NonNull NodeController child, int index) { + view.removeView(child.getView()); + view.addView(child.getView(), index); + } + + @Override + public void removeChild(@NonNull NodeController child, boolean isTransfer) { + view.removeView(child.getView()); + } + } + + private static class SpecBuilder { + private final NodeController mController; + private final SpecBuilder[] mChildren; + + SpecBuilder(NodeController controller, SpecBuilder... children) { + mController = controller; + mChildren = children; + } + + public NodeSpec build() { + return build(null); + } + + public NodeSpec build(@Nullable NodeSpec parent) { + final NodeSpecImpl spec = new NodeSpecImpl(parent, mController); + for (SpecBuilder childBuilder : mChildren) { + spec.getChildren().add(childBuilder.build(spec)); + } + return spec; + } + } + + private static SpecBuilder node(NodeController controller, SpecBuilder... children) { + return new SpecBuilder(controller, children); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java deleted file mode 100644 index 43d8b50bcf72..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.systemui.statusbar.notification.row; - -import static android.app.AppOpsManager.OP_CAMERA; -import static android.app.AppOpsManager.OP_RECORD_AUDIO; -import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertTrue; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.Notification; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.graphics.drawable.Drawable; -import android.os.UserHandle; -import android.service.notification.StatusBarNotification; -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.UiThreadTest; -import android.util.ArraySet; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import com.android.internal.logging.testing.UiEventLoggerFake; -import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.concurrent.CountDownLatch; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@UiThreadTest -public class AppOpsInfoTest extends SysuiTestCase { - private static final String TEST_PACKAGE_NAME = "test_package"; - private static final int TEST_UID = 1; - - private AppOpsInfo mAppOpsInfo; - private final PackageManager mMockPackageManager = mock(PackageManager.class); - private final NotificationGuts mGutsParent = mock(NotificationGuts.class); - private StatusBarNotification mSbn; - private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake(); - - @Before - public void setUp() throws Exception { - // Inflate the layout - final LayoutInflater layoutInflater = LayoutInflater.from(mContext); - mAppOpsInfo = (AppOpsInfo) layoutInflater.inflate(R.layout.app_ops_info, null); - mAppOpsInfo.setGutsParent(mGutsParent); - - // PackageManager must return a packageInfo and applicationInfo. - final PackageInfo packageInfo = new PackageInfo(); - packageInfo.packageName = TEST_PACKAGE_NAME; - when(mMockPackageManager.getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt())) - .thenReturn(packageInfo); - final ApplicationInfo applicationInfo = new ApplicationInfo(); - applicationInfo.uid = TEST_UID; // non-zero - when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn( - applicationInfo); - - mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, - new Notification(), UserHandle.CURRENT, null, 0); - } - - @Test - public void testBindNotification_SetsTextApplicationName() { - when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); - mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>()); - final TextView textView = mAppOpsInfo.findViewById(R.id.pkgname); - assertTrue(textView.getText().toString().contains("App Name")); - } - - @Test - public void testBindNotification_SetsPackageIcon() { - final Drawable iconDrawable = mock(Drawable.class); - when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class))) - .thenReturn(iconDrawable); - mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>()); - final ImageView iconView = mAppOpsInfo.findViewById(R.id.pkgicon); - assertEquals(iconDrawable, iconView.getDrawable()); - } - - @Test - public void testBindNotification_SetsOnClickListenerForSettings() throws Exception { - ArraySet<Integer> expectedOps = new ArraySet<>(); - expectedOps.add(OP_CAMERA); - final CountDownLatch latch = new CountDownLatch(1); - mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid, - ArraySet<Integer> ops) -> { - assertEquals(TEST_PACKAGE_NAME, pkg); - assertEquals(expectedOps, ops); - assertEquals(TEST_UID, uid); - latch.countDown(); - }, mSbn, mUiEventLogger, expectedOps); - - final View settingsButton = mAppOpsInfo.findViewById(R.id.settings); - settingsButton.performClick(); - // Verify that listener was triggered. - assertEquals(0, latch.getCount()); - } - - @Test - public void testBindNotification_LogsOpen() throws Exception { - mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>()); - assertEquals(1, mUiEventLogger.numLogs()); - assertEquals(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_OPEN.getId(), - mUiEventLogger.eventId(0)); - } - - @Test - public void testOk() { - ArraySet<Integer> expectedOps = new ArraySet<>(); - expectedOps.add(OP_CAMERA); - final CountDownLatch latch = new CountDownLatch(1); - mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid, - ArraySet<Integer> ops) -> { - assertEquals(TEST_PACKAGE_NAME, pkg); - assertEquals(expectedOps, ops); - assertEquals(TEST_UID, uid); - latch.countDown(); - }, mSbn, mUiEventLogger, expectedOps); - - final View okButton = mAppOpsInfo.findViewById(R.id.ok); - okButton.performClick(); - assertEquals(1, latch.getCount()); - verify(mGutsParent, times(1)).closeControls(eq(okButton), anyBoolean()); - } - - @Test - public void testPrompt_camera() { - ArraySet<Integer> expectedOps = new ArraySet<>(); - expectedOps.add(OP_CAMERA); - mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); - TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); - assertEquals("This app is using the camera.", prompt.getText()); - } - - @Test - public void testPrompt_mic() { - ArraySet<Integer> expectedOps = new ArraySet<>(); - expectedOps.add(OP_RECORD_AUDIO); - mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); - TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); - assertEquals("This app is using the microphone.", prompt.getText()); - } - - @Test - public void testPrompt_overlay() { - ArraySet<Integer> expectedOps = new ArraySet<>(); - expectedOps.add(OP_SYSTEM_ALERT_WINDOW); - mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); - TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); - assertEquals("This app is displaying over other apps on your screen.", prompt.getText()); - } - - @Test - public void testPrompt_camera_mic() { - ArraySet<Integer> expectedOps = new ArraySet<>(); - expectedOps.add(OP_CAMERA); - expectedOps.add(OP_RECORD_AUDIO); - mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); - TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); - assertEquals("This app is using the microphone and camera.", prompt.getText()); - } - - @Test - public void testPrompt_camera_mic_overlay() { - ArraySet<Integer> expectedOps = new ArraySet<>(); - expectedOps.add(OP_CAMERA); - expectedOps.add(OP_RECORD_AUDIO); - expectedOps.add(OP_SYSTEM_ALERT_WINDOW); - mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); - TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); - assertEquals("This app is displaying over other apps on your screen and using" - + " the microphone and camera.", prompt.getText()); - } - - @Test - public void testPrompt_camera_overlay() { - ArraySet<Integer> expectedOps = new ArraySet<>(); - expectedOps.add(OP_CAMERA); - expectedOps.add(OP_SYSTEM_ALERT_WINDOW); - mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); - TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); - assertEquals("This app is displaying over other apps on your screen and using" - + " the camera.", prompt.getText()); - } - - @Test - public void testPrompt_mic_overlay() { - ArraySet<Integer> expectedOps = new ArraySet<>(); - expectedOps.add(OP_RECORD_AUDIO); - expectedOps.add(OP_SYSTEM_ALERT_WINDOW); - mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps); - TextView prompt = mAppOpsInfo.findViewById(R.id.prompt); - assertEquals("This app is displaying over other apps on your screen and using" - + " the microphone.", prompt.getText()); - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index dc4a6ca14a77..f29b46c73e4d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -35,12 +35,10 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.AppOpsManager; import android.app.NotificationChannel; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; -import android.util.ArraySet; import android.view.View; import androidx.test.filters.SmallTest; @@ -213,46 +211,6 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { } @Test - public void testShowAppOps_noHeader() { - // public notification is custom layout - no header - mGroupRow.setSensitive(true, true); - mGroupRow.setAppOpsOnClickListener(null); - mGroupRow.showAppOpsIcons(null); - } - - @Test - public void testShowAppOpsIcons_header() { - NotificationContentView publicLayout = mock(NotificationContentView.class); - mGroupRow.setPublicLayout(publicLayout); - NotificationContentView privateLayout = mock(NotificationContentView.class); - mGroupRow.setPrivateLayout(privateLayout); - NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class); - when(mockContainer.getNotificationChildCount()).thenReturn(1); - mGroupRow.setChildrenContainer(mockContainer); - - ArraySet<Integer> ops = new ArraySet<>(); - ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS); - mGroupRow.showAppOpsIcons(ops); - - verify(mockContainer, times(1)).showAppOpsIcons(ops); - verify(privateLayout, times(1)).showAppOpsIcons(ops); - verify(publicLayout, times(1)).showAppOpsIcons(ops); - - } - - @Test - public void testAppOpsOnClick() { - ExpandableNotificationRow.CoordinateOnClickListener l = mock( - ExpandableNotificationRow.CoordinateOnClickListener.class); - View view = mock(View.class); - - mGroupRow.setAppOpsOnClickListener(l); - - mGroupRow.getAppOpsOnClickListener().onClick(view); - verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any()); - } - - @Test public void testFeedback_noHeader() { // public notification is custom layout - no header mGroupRow.setSensitive(true, true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java index 6d4a7115b8c4..c2091da2347e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java @@ -76,32 +76,6 @@ public class NotificationContentViewTest extends SysuiTestCase { @Test @UiThreadTest - public void testShowAppOpsIcons() { - View mockContracted = mock(NotificationHeaderView.class); - when(mockContracted.findViewById(com.android.internal.R.id.mic)) - .thenReturn(mockContracted); - View mockExpanded = mock(NotificationHeaderView.class); - when(mockExpanded.findViewById(com.android.internal.R.id.mic)) - .thenReturn(mockExpanded); - View mockHeadsUp = mock(NotificationHeaderView.class); - when(mockHeadsUp.findViewById(com.android.internal.R.id.mic)) - .thenReturn(mockHeadsUp); - - mView.setContractedChild(mockContracted); - mView.setExpandedChild(mockExpanded); - mView.setHeadsUpChild(mockHeadsUp); - - ArraySet<Integer> ops = new ArraySet<>(); - ops.add(AppOpsManager.OP_RECORD_AUDIO); - mView.showAppOpsIcons(ops); - - verify(mockContracted, times(1)).setVisibility(View.VISIBLE); - verify(mockExpanded, times(1)).setVisibility(View.VISIBLE); - verify(mockHeadsUp, times(1)).setVisibility(View.VISIBLE); - } - - @Test - @UiThreadTest public void testShowFeedbackIcon() { View mockContracted = mock(NotificationHeaderView.class); when(mockContracted.findViewById(com.android.internal.R.id.feedback)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java index 83fc82634b60..3c5aa1ae9519 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java @@ -79,7 +79,6 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.bubbles.BubblesTestActivity; import com.android.systemui.statusbar.SbnBuilder; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.phone.ShadeController; @@ -135,7 +134,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { @Mock private PackageManager mMockPackageManager; @Mock - private VisualStabilityManager mVisualStabilityManager; + private OnUserInteractionCallback mOnUserInteractionCallback; @Mock private BubbleController mBubbleController; @Mock @@ -244,7 +243,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -268,7 +267,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -293,7 +292,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mLauncherApps, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -319,7 +318,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -344,7 +343,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -368,7 +367,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -403,7 +402,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, entry, @@ -428,7 +427,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -457,7 +456,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -481,7 +480,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -509,7 +508,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -537,7 +536,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -568,7 +567,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -598,7 +597,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -642,7 +641,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -685,7 +684,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -729,7 +728,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -766,7 +765,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -802,7 +801,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -840,7 +839,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -876,7 +875,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -912,7 +911,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -947,7 +946,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -981,7 +980,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -1006,7 +1005,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -1041,7 +1040,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, @@ -1081,7 +1080,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mShortcutManager, mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, TEST_PACKAGE_NAME, mNotificationChannel, mEntry, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index a90af87064b5..7a0a19bd5424 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -221,6 +221,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { .thenAnswer((Answer<ExpandableNotificationRowController>) invocation -> new ExpandableNotificationRowController( viewCaptor.getValue(), + mListContainer, mock(ActivatableNotificationViewController.class), mNotificationMediaManager, mock(PluginManager.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index 14994d55852f..c2c40cac3d0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -74,12 +74,11 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -114,10 +113,10 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Rule public MockitoRule mockito = MockitoJUnit.rule(); @Mock private MetricsLogger mMetricsLogger; - @Mock private VisualStabilityManager mVisualStabilityManager; + @Mock private OnUserInteractionCallback mOnUserInteractionCallback; @Mock private NotificationPresenter mPresenter; @Mock private NotificationActivityStarter mNotificationActivityStarter; - @Mock private NotificationStackScrollLayout mStackScroller; + @Mock private NotificationListContainer mNotificationListContainer; @Mock private NotificationInfo.CheckSaveListener mCheckSaveListener; @Mock private OnSettingsClickListener mOnSettingsClickListener; @Mock private DeviceProvisionedController mDeviceProvisionedController; @@ -143,20 +142,22 @@ public class NotificationGutsManagerTest extends SysuiTestCase { mDependency.injectTestDependency(DeviceProvisionedController.class, mDeviceProvisionedController); mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); - mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager); + mDependency.injectTestDependency( + OnUserInteractionCallback.class, + mOnUserInteractionCallback); mDependency.injectTestDependency(BubbleController.class, mBubbleController); mDependency.injectMockDependency(NotificationLockscreenUserManager.class); mHandler = Handler.createAsync(mTestableLooper.getLooper()); mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this)); when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); - mGutsManager = new NotificationGutsManager(mContext, mVisualStabilityManager, + mGutsManager = new NotificationGutsManager(mContext, () -> mStatusBar, mHandler, mHandler, mAccessibilityManager, mHighPriorityProvider, mINotificationManager, mLauncherApps, mShortcutManager, mChannelEditorDialogController, mContextTracker, mProvider, mAssistantFeedbackController, mBubbleController, - new UiEventLoggerFake()); - mGutsManager.setUpWithPresenter(mPresenter, mStackScroller, + new UiEventLoggerFake(), mOnUserInteractionCallback); + mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer, mCheckSaveListener, mOnSettingsClickListener); mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); } @@ -360,7 +361,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { verify(notificationInfoView).bindNotification( any(PackageManager.class), any(INotificationManager.class), - eq(mVisualStabilityManager), + eq(mOnUserInteractionCallback), eq(mChannelEditorDialogController), eq(statusBarNotification.getPackageName()), any(NotificationChannel.class), @@ -394,7 +395,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { verify(notificationInfoView).bindNotification( any(PackageManager.class), any(INotificationManager.class), - eq(mVisualStabilityManager), + eq(mOnUserInteractionCallback), eq(mChannelEditorDialogController), eq(statusBarNotification.getPackageName()), any(NotificationChannel.class), @@ -426,7 +427,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { verify(notificationInfoView).bindNotification( any(PackageManager.class), any(INotificationManager.class), - eq(mVisualStabilityManager), + eq(mOnUserInteractionCallback), eq(mChannelEditorDialogController), eq(statusBarNotification.getPackageName()), any(NotificationChannel.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index fd8b72bb15db..02a3e11f312b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -66,7 +66,6 @@ import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; @@ -114,7 +113,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Mock private PackageManager mMockPackageManager; @Mock - private VisualStabilityManager mVisualStabilityManager; + private OnUserInteractionCallback mOnUserInteractionCallback; @Mock private ChannelEditorDialogController mChannelEditorDialogController; @@ -181,7 +180,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -207,7 +206,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -229,7 +228,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -260,7 +259,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -283,7 +282,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -311,7 +310,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -334,7 +333,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -356,7 +355,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mDefaultNotificationChannel, @@ -382,7 +381,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mDefaultNotificationChannel, @@ -404,7 +403,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -427,7 +426,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -455,7 +454,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -478,7 +477,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -502,7 +501,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -518,7 +517,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -541,7 +540,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), @@ -569,7 +568,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -593,7 +592,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -617,7 +616,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -643,7 +642,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -665,7 +664,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -688,7 +687,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -709,7 +708,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -730,7 +729,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -751,7 +750,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -774,7 +773,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -798,7 +797,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -825,7 +824,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -852,7 +851,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -879,7 +878,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -914,7 +913,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -942,7 +941,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -982,7 +981,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1017,7 +1016,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1047,7 +1046,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1082,7 +1081,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1120,7 +1119,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1157,7 +1156,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1175,7 +1174,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.findViewById(R.id.done).performClick(); mNotificationInfo.handleCloseControls(true, false); - verify(mVisualStabilityManager).temporarilyAllowReordering(); + verify(mOnUserInteractionCallback).onImportanceChanged(mEntry); } @Test @@ -1185,7 +1184,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1216,7 +1215,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1250,7 +1249,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1283,7 +1282,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1316,7 +1315,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, @@ -1342,7 +1341,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, - mVisualStabilityManager, + mOnUserInteractionCallback, mChannelEditorDialogController, TEST_PACKAGE_NAME, mNotificationChannel, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 8ccbb2ebb0db..9a8678f01870 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -52,6 +52,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.notification.ConversationNotificationProcessor; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -68,7 +69,6 @@ import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationGroupManager; -import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.SmartReplyConstants; import org.mockito.ArgumentCaptor; @@ -422,10 +422,10 @@ public class NotificationTestHelper { mock(OnExpandClickListener.class), mock(NotificationMediaManager.class), mock(ExpandableNotificationRow.CoordinateOnClickListener.class), - mock(ExpandableNotificationRow.CoordinateOnClickListener.class), mock(FalsingManager.class), mStatusBarStateController, - mPeopleNotificationIdentifier); + mPeopleNotificationIdentifier, + mock(OnUserInteractionCallback.class)); row.setAboveShelfChangedListener(aboveShelf -> { }); mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags); inflateAndWait(entry); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 85701c24f7d6..9a6674e165e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -74,13 +74,13 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger; import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -212,7 +212,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { // holds a copy of the CUT's instances of these KeyguardBypassController, so they still // refer to the CUT's member variables, not the spy's member variables. mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null, - true /* allowLongPress */, mNotificationRoundnessManager, + mNotificationRoundnessManager, mock(DynamicPrivacyController.class), mock(SysuiStatusBarStateController.class), mHeadsUpManager, @@ -239,7 +239,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mStackScroller.setScrimController(mock(ScrimController.class)); mStackScroller.setGroupManager(mGroupManager); mStackScroller.setEmptyShadeView(mEmptyShadeView); - mStackScroller.setIconAreaController(mNotificationIconAreaController); // Stub out functionality that isn't necessary to test. doNothing().when(mBar) @@ -437,9 +436,9 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - public void testOnDensityOrFontScaleChanged_reInflatesFooterViews() { + public void testReInflatesFooterViews() { clearInvocations(mStackScroller); - mStackScroller.onDensityOrFontScaleChanged(); + mStackScroller.reinflateViews(); verify(mStackScroller).setFooterView(any()); verify(mStackScroller).setEmptyShadeView(any()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java new file mode 100644 index 000000000000..e3acf0213725 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.statusbar.notification.stack; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.tuner.TunerService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link NotificationStackScrollLayoutController}. + */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class NotificationStackScrollerControllerTest extends SysuiTestCase { + + @Mock + private NotificationGutsManager mNotificationGutsManager; + @Mock + private HeadsUpManagerPhone mHeadsUpManager; + @Mock + private NotificationRoundnessManager mNotificationRoundnessManager; + @Mock + private TunerService mTunerService; + @Mock + private AmbientState mAmbientState; + @Mock + private DynamicPrivacyController mDynamicPrivacyController; + @Mock + private ConfigurationController mConfigurationController; + @Mock + private NotificationStackScrollLayout mNotificationStackScrollLayout; + + NotificationStackScrollLayoutController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mController = new NotificationStackScrollLayoutController( + true, + mNotificationGutsManager, + mHeadsUpManager, + mNotificationRoundnessManager, + mTunerService, + mDynamicPrivacyController, + mConfigurationController + ); + + when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true); + } + + + @Test + public void testAttach_viewAlreadyAttached() { + mController.attach(mNotificationStackScrollLayout); + + verify(mConfigurationController).addCallback( + any(ConfigurationController.ConfigurationListener.class)); + } + @Test + public void testAttach_viewAttachedAfterInit() { + when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(false); + + mController.attach(mNotificationStackScrollLayout); + + verify(mConfigurationController, never()).addCallback( + any(ConfigurationController.ConfigurationListener.class)); + + mController.mOnAttachStateChangeListener.onViewAttachedToWindow( + mNotificationStackScrollLayout); + + verify(mConfigurationController).addCallback( + any(ConfigurationController.ConfigurationListener.class)); + } + + @Test + public void testOnDensityOrFontScaleChanged_reInflatesFooterViews() { + mController.attach(mNotificationStackScrollLayout); + mController.mConfigurationListener.onDensityOrFontScaleChanged(); + verify(mNotificationStackScrollLayout).reinflateViews(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index 64907eef2dd0..f1c8ece58fda 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -43,6 +43,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; @@ -76,7 +77,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { @Mock private ScrimController mScrimController; @Mock - private StatusBar mStatusBar; + private BiometricUnlockController.BiometricModeListener mBiometricModeListener; @Mock private ShadeController mShadeController; @Mock @@ -105,11 +106,12 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mDependency.injectTestDependency(NotificationMediaManager.class, mMediaManager); res.addOverride(com.android.internal.R.integer.config_wakeUpDelayDoze, 0); mBiometricUnlockController = new BiometricUnlockController(mContext, mDozeScrimController, - mKeyguardViewMediator, mScrimController, mStatusBar, mShadeController, + mKeyguardViewMediator, mScrimController, mShadeController, mNotificationShadeWindowController, mKeyguardStateController, mHandler, mUpdateMonitor, res.getResources(), mKeyguardBypassController, mDozeParameters, mMetricsLogger, mDumpManager); mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager); + mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java index a6e29181e435..37ccac0b23b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java @@ -36,15 +36,16 @@ import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.assist.AssistManager; +import com.android.systemui.biometrics.AuthController; import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -68,7 +69,6 @@ public class DozeServiceHostTest extends SysuiTestCase { @Mock private HeadsUpManagerPhone mHeadsUpManager; @Mock private ScrimController mScrimController; @Mock private DozeScrimController mDozeScrimController; - @Mock private VisualStabilityManager mVisualStabilityManager; @Mock private KeyguardViewMediator mKeyguardViewMediator; @Mock private StatusBarStateControllerImpl mStatusBarStateController; @Mock private BatteryController mBatteryController; @@ -89,6 +89,7 @@ public class DozeServiceHostTest extends SysuiTestCase { @Mock private View mAmbientIndicationContainer; @Mock private BiometricUnlockController mBiometricUnlockController; @Mock private LockscreenLockIconController mLockscreenLockIconController; + @Mock private AuthController mAuthController; @Before public void setup() { @@ -97,13 +98,16 @@ public class DozeServiceHostTest extends SysuiTestCase { mStatusBarStateController, mDeviceProvisionedController, mHeadsUpManager, mBatteryController, mScrimController, () -> mBiometricUnlockController, mKeyguardViewMediator, () -> mAssistManager, mDozeScrimController, - mKeyguardUpdateMonitor, mVisualStabilityManager, mPulseExpansionHandler, + mKeyguardUpdateMonitor, mPulseExpansionHandler, mNotificationShadeWindowController, mNotificationWakeUpCoordinator, - mLockscreenLockIconController); - - mDozeServiceHost.initialize(mStatusBar, mNotificationIconAreaController, - mStatusBarKeyguardViewManager, mNotificationShadeWindowViewController, - mNotificationPanel, mAmbientIndicationContainer); + mLockscreenLockIconController, mAuthController, mNotificationIconAreaController); + + mDozeServiceHost.initialize( + mStatusBar, + mStatusBarKeyguardViewManager, + mNotificationShadeWindowViewController, + mNotificationPanel, + mAmbientIndicationContainer); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java index e546dff8abf6..8dea84c4d0b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java @@ -38,7 +38,7 @@ import com.android.systemui.statusbar.HeadsUpStatusBarView; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Assert; @@ -51,8 +51,8 @@ import org.junit.runner.RunWith; @RunWithLooper public class HeadsUpAppearanceControllerTest extends SysuiTestCase { - private final NotificationStackScrollLayout mStackScroller = - mock(NotificationStackScrollLayout.class); + private final NotificationStackScrollLayoutController mStackScrollerController = + mock(NotificationStackScrollLayoutController.class); private final NotificationPanelViewController mPanelView = mock(NotificationPanelViewController.class); private final DarkIconDispatcher mDarkIconDispatcher = mock(DarkIconDispatcher.class); @@ -93,9 +93,9 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mWakeUpCoordinator, mKeyguardStateController, mCommandQueue, - mHeadsUpStatusBarView, - mStackScroller, + mStackScrollerController, mPanelView, + mHeadsUpStatusBarView, new View(mContext), mOperatorNameView, new View(mContext)); @@ -172,9 +172,9 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mWakeUpCoordinator, mKeyguardStateController, mCommandQueue, - mHeadsUpStatusBarView, - mStackScroller, + mStackScrollerController, mPanelView, + mHeadsUpStatusBarView, new View(mContext), new View(mContext), new View(mContext)); @@ -193,14 +193,14 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { reset(mHeadsUpManager); reset(mDarkIconDispatcher); reset(mPanelView); - reset(mStackScroller); + reset(mStackScrollerController); mHeadsUpAppearanceController.destroy(); verify(mHeadsUpManager).removeListener(any()); verify(mDarkIconDispatcher).removeDarkReceiver((DarkIconDispatcher.DarkReceiver) any()); verify(mPanelView).removeVerticalTranslationListener(any()); verify(mPanelView).removeTrackingHeadsUpListener(any()); verify(mPanelView).setHeadsUpAppearanceController(any()); - verify(mStackScroller).removeOnExpandedHeightChangedListener(any()); - verify(mStackScroller).removeOnLayoutChangeListener(any()); + verify(mStackScrollerController).removeOnExpandedHeightChangedListener(any()); + verify(mStackScrollerController).removeOnLayoutChangeListener(any()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java index 6d642ec44314..2239b1b96ac8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java @@ -33,9 +33,10 @@ import com.android.systemui.bubbles.BubbleController; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.AlertingNotificationManagerTest; -import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java index 3e469073694f..ccdc69aeaa18 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java @@ -34,6 +34,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.view.AppearanceRegion; import com.android.systemui.SysuiTestCase; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.statusbar.policy.BatteryController; import org.junit.Before; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java index d1a439f99702..5222ffff2637 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java @@ -17,8 +17,6 @@ package com.android.systemui.statusbar.phone; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -29,10 +27,13 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.demomode.DemoModeController; +import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import org.junit.Before; import org.junit.Test; @@ -48,8 +49,6 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase { @Mock private NotificationListener mListener; @Mock - StatusBar mStatusBar; - @Mock StatusBarStateController mStatusBarStateController; @Mock NotificationWakeUpCoordinator mWakeUpCoordinator; @@ -58,26 +57,37 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase { @Mock NotificationMediaManager mNotificationMediaManager; @Mock - NotificationIconContainer mNotificationIconContainer; - @Mock DozeParameters mDozeParameters; @Mock - NotificationShadeWindowView mNotificationShadeWindowView; + CommonNotifCollection mNotifCollection; + @Mock + DarkIconDispatcher mDarkIconDispatcher; + @Mock + StatusBarWindowController mStatusBarWindowController; private NotificationIconAreaController mController; @Mock private BubbleController mBubbleController; + @Mock private DemoModeController mDemoModeController; + @Mock + private NotificationIconContainer mAodIcons; @Before public void setup() { MockitoAnnotations.initMocks(this); - when(mStatusBar.getNotificationShadeWindowView()).thenReturn(mNotificationShadeWindowView); - when(mNotificationShadeWindowView.findViewById(anyInt())).thenReturn( - mNotificationIconContainer); - - mController = new NotificationIconAreaController(mContext, mStatusBar, - mStatusBarStateController, mWakeUpCoordinator, mKeyguardBypassController, - mNotificationMediaManager, mListener, mDozeParameters, mBubbleController); + mController = new NotificationIconAreaController( + mContext, + mStatusBarStateController, + mWakeUpCoordinator, + mKeyguardBypassController, + mNotificationMediaManager, + mListener, + mDozeParameters, + mBubbleController, + mDemoModeController, + mDarkIconDispatcher, + mStatusBarWindowController); + mController.setupAodIcons(mAodIcons); } @Test @@ -98,7 +108,7 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase { public void testAppearResetsTranslation() { when(mDozeParameters.shouldControlScreenOff()).thenReturn(false); mController.appearAodIcons(); - verify(mNotificationIconContainer).setTranslationY(0); - verify(mNotificationIconContainer).setAlpha(1.0f); + verify(mAodIcons).setTranslationY(0); + verify(mAodIcons).setAlpha(1.0f); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index bf4ccd22effd..c9e9d94d8a79 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -74,6 +74,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ZenModeController; @@ -186,7 +187,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Mock private KeyguardClockSwitchController mKeyguardClockSwitchController; - private FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder; + @Mock + private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; private NotificationPanelViewController mNotificationPanelViewController; private View.AccessibilityDelegate mAccessibiltyDelegate; @@ -205,14 +207,18 @@ public class NotificationPanelViewTest extends SysuiTestCase { when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch); when(mView.findViewById(R.id.notification_stack_scroller)) .thenReturn(mNotificationStackScrollLayout); - when(mNotificationStackScrollLayout.getHeight()).thenReturn(1000); - when(mNotificationStackScrollLayout.getHeadsUpCallback()).thenReturn(mHeadsUpCallback); + when(mNotificationStackScrollLayout.getController()) + .thenReturn(mNotificationStackScrollLayoutController); + when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000); + when(mNotificationStackScrollLayoutController.getHeadsUpCallback()) + .thenReturn(mHeadsUpCallback); when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea); when(mKeyguardBottomArea.getLeftView()).thenReturn(mock(KeyguardAffordanceView.class)); when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class)); when(mView.findViewById(R.id.big_clock_container)).thenReturn(mBigClockContainer); when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); - mFlingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(mDisplayMetrics); + FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder( + mDisplayMetrics); doAnswer((Answer<Void>) invocation -> { mTouchHandler = invocation.getArgument(0); @@ -241,19 +247,23 @@ public class NotificationPanelViewTest extends SysuiTestCase { mDozeParameters, mCommandQueue, mVibratorHelper, mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor, mMetricsLogger, mActivityManager, mZenModeController, mConfigurationController, - mFlingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, + flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, mConversationNotificationManager, mMediaHiearchyManager, mBiometricUnlockController, mStatusBarKeyguardViewManager, - () -> mKeyguardClockSwitchController); - mNotificationPanelViewController.initDependencies(mStatusBar, mGroupManager, - mNotificationShelfController, mNotificationAreaController, mScrimController); + () -> mKeyguardClockSwitchController, + mNotificationStackScrollLayoutController, + mNotificationAreaController); + mNotificationPanelViewController.initDependencies( + mStatusBar, + mGroupManager, + mNotificationShelfController, + mScrimController); mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager); mNotificationPanelViewController.setBar(mPanelBar); ArgumentCaptor<View.AccessibilityDelegate> accessibilityDelegateArgumentCaptor = ArgumentCaptor.forClass(View.AccessibilityDelegate.class); - verify(mView) - .setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture()); + verify(mView).setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture()); mAccessibiltyDelegate = accessibilityDelegateArgumentCaptor.getValue(); } @@ -261,8 +271,10 @@ public class NotificationPanelViewTest extends SysuiTestCase { public void testSetDozing_notifiesNsslAndStateController() { mNotificationPanelViewController.setDozing(true /* dozing */, true /* animate */, null /* touch */); - InOrder inOrder = inOrder(mNotificationStackScrollLayout, mStatusBarStateController); - inOrder.verify(mNotificationStackScrollLayout).setDozing(eq(true), eq(true), eq(null)); + InOrder inOrder = inOrder( + mNotificationStackScrollLayoutController, mStatusBarStateController); + inOrder.verify(mNotificationStackScrollLayoutController) + .setDozing(eq(true), eq(true), eq(null)); inOrder.verify(mStatusBarStateController).setDozeAmount(eq(1f), eq(true)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java index 8c37cf1514fd..fcea17c5a6b5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java @@ -57,7 +57,7 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @RunWithLooper @SmallTest -public class NotificationShadeWindowControllerTest extends SysuiTestCase { +public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { @Mock private WindowManager mWindowManager; @Mock private DozeParameters mDozeParameters; @@ -72,7 +72,7 @@ public class NotificationShadeWindowControllerTest extends SysuiTestCase { @Mock private DumpManager mDumpManager; @Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters; - private NotificationShadeWindowController mNotificationShadeWindowController; + private NotificationShadeWindowControllerImpl mNotificationShadeWindowController; @Before public void setUp() { @@ -80,7 +80,7 @@ public class NotificationShadeWindowControllerTest extends SysuiTestCase { when(mDozeParameters.getAlwaysOn()).thenReturn(true); when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors); - mNotificationShadeWindowController = new NotificationShadeWindowController(mContext, + mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController, mColorExtractor, mDumpManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java index e04d25b17c71..c1d51f3beb7d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java @@ -38,6 +38,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShadeDepthController; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; @@ -100,7 +101,9 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { mController = new NotificationShadeWindowViewController( new InjectionInflationController( - SystemUIFactory.getInstance().getRootComponent()), + SystemUIFactory.getInstance() + .getSysUIComponent() + .createViewInstanceCreatorFactory()), mCoordinator, mPulseExpansionHandler, mDynamicPrivacyController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 675a2df768d8..ccc307841491 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -43,9 +43,11 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.DismissCallbackRegistry; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index 33067343ba40..3f631b1f6282 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -73,7 +73,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; -import com.android.systemui.statusbar.notification.row.OnDismissCallback; +import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -132,7 +132,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { @Mock private Intent mContentIntentInner; @Mock - private OnDismissCallback mOnDismissCallback; + private OnUserInteractionCallback mOnUserInteractionCallback; @Mock private NotificationActivityStarter mNotificationActivityStarter; private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -184,7 +184,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { getContext(), mock(CommandQueue.class), mHandler, - mHandler, mUiBgExecutor, mEntryManager, mNotifPipeline, @@ -211,7 +210,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mFeatureFlags, mock(MetricsLogger.class), mock(StatusBarNotificationActivityStarterLogger.class), - mOnDismissCallback) + mOnUserInteractionCallback) .setStatusBar(mStatusBar) .setNotificationPresenter(mock(NotificationPresenter.class)) .setNotificationPanelViewController(mock(NotificationPanelViewController.class)) @@ -234,9 +233,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // set up Handler to synchronously invoke the Runnable arg doAnswer(answerVoid(Runnable::run)) .when(mHandler).post(any(Runnable.class)); - - doAnswer(answerVoid(Runnable::run)) - .when(mHandler).postAtFrontOfQueue(any(Runnable.class)); } @Test @@ -271,7 +267,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { eq(sbn.getKey()), any(NotificationVisibility.class)); // Notification calls dismiss callback to remove notification due to FLAG_AUTO_CANCEL - verify(mOnDismissCallback).onDismiss(mNotificationRow.getEntry(), REASON_CLICK); + verify(mOnUserInteractionCallback).onDismiss(mNotificationRow.getEntry(), REASON_CLICK); } @Test @@ -300,7 +296,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { verifyZeroInteractions(mContentIntent); // Notification should not be cancelled. - verify(mOnDismissCallback, never()).onDismiss(eq(mNotificationRow.getEntry()), anyInt()); + verify(mOnUserInteractionCallback, never()).onDismiss(eq(mNotificationRow.getEntry()), + anyInt()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 318e9b87fa70..c0ebfadf6b57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -23,13 +23,11 @@ import static org.mockito.Mockito.when; import android.app.Notification; import android.app.StatusBarManager; -import android.content.Context; import android.metrics.LogMaker; import android.support.test.metricshelper.MetricsAsserts; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; -import android.view.ViewGroup; import androidx.test.filters.SmallTest; @@ -45,20 +43,23 @@ import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; @@ -112,11 +113,17 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { NotificationShadeWindowView notificationShadeWindowView = mock(NotificationShadeWindowView.class); + NotificationStackScrollLayoutController stackScrollLayoutController = + mock(NotificationStackScrollLayoutController.class); + when(stackScrollLayoutController.getView()).thenReturn( + mock(NotificationStackScrollLayout.class)); + when(stackScrollLayoutController.getNotificationListContainer()).thenReturn( + mock(NotificationListContainer.class)); when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources()); mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(mContext, mock(NotificationPanelViewController.class), mock(HeadsUpManagerPhone.class), - notificationShadeWindowView, mock(NotificationListContainerViewGroup.class), + notificationShadeWindowView, stackScrollLayoutController, mock(DozeScrimController.class), mock(ScrimController.class), mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class), mock(KeyguardStateController.class), @@ -205,14 +212,4 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { new LogMaker(MetricsEvent.ACTION_LS_NOTE) .setType(MetricsEvent.TYPE_ACTION)); } - - // We need this because mockito doesn't know how to construct a mock that extends ViewGroup - // and implements NotificationListContainer without it because of classloader issues. - private abstract static class NotificationListContainerViewGroup extends ViewGroup - implements NotificationListContainer { - - public NotificationListContainerViewGroup(Context context) { - super(context); - } - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 5a08c9ca017b..8462386aaf2d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -83,10 +83,12 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.PluginDependencyProvider; @@ -97,12 +99,12 @@ import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; -import com.android.systemui.statusbar.NavigationBarController; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.RemoteInputController; @@ -115,16 +117,18 @@ import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerFake; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -171,6 +175,8 @@ public class StatusBarTest extends SysuiTestCase { @Mock private KeyguardStateController mKeyguardStateController; @Mock private KeyguardIndicationController mKeyguardIndicationController; @Mock private NotificationStackScrollLayout mStackScroller; + @Mock private NotificationStackScrollLayoutController mStackScrollerController; + @Mock private NotificationListContainer mNotificationListContainer; @Mock private HeadsUpManagerPhone mHeadsUpManager; @Mock private NotificationPanelViewController mNotificationPanelViewController; @Mock private NotificationPanelView mNotificationPanelView; @@ -248,6 +254,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private ExtensionController mExtensionController; @Mock private UserInfoControllerImpl mUserInfoControllerImpl; @Mock private PhoneStatusBarPolicy mPhoneStatusBarPolicy; + @Mock private DemoModeController mDemoModeController; @Mock private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy; private ShadeController mShadeController; private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -283,6 +290,10 @@ public class StatusBarTest extends SysuiTestCase { mContext.setTheme(R.style.Theme_SystemUI_Light); + when(mStackScroller.getController()).thenReturn(mStackScrollerController); + when(mStackScrollerController.getView()).thenReturn(mStackScroller); + when(mStackScrollerController.getNotificationListContainer()).thenReturn( + mNotificationListContainer); when(mStackScroller.generateLayoutParams(any())).thenReturn(new LayoutParams(0, 0)); when(mNotificationPanelViewController.getView()).thenReturn(mNotificationPanelView); when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0)); @@ -394,7 +405,6 @@ public class StatusBarTest extends SysuiTestCase { mStatusBarKeyguardViewManager, mViewMediatorCallback, mInitController, - mDarkIconDispatcher, new Handler(TestableLooper.get(this).getLooper()), mPluginDependencyProvider, mKeyguardDismissUtil, @@ -403,8 +413,10 @@ public class StatusBarTest extends SysuiTestCase { mPhoneStatusBarPolicy, mKeyguardIndicationController, mDismissCallbackRegistry, + mDemoModeController, mNotificationShadeDepthControllerLazy, - mStatusBarTouchableRegionManager); + mStatusBarTouchableRegionManager, + mNotificationIconAreaController); when(mNotificationShadeWindowView.findViewById(R.id.lock_icon_container)).thenReturn( mLockIconContainer); @@ -422,14 +434,13 @@ public class StatusBarTest extends SysuiTestCase { mStatusBar.mNotificationShadeWindowView = mNotificationShadeWindowView; mStatusBar.mNotificationPanelViewController = mNotificationPanelViewController; mStatusBar.mDozeScrimController = mDozeScrimController; - mStatusBar.mNotificationIconAreaController = mNotificationIconAreaController; mStatusBar.mPresenter = mNotificationPresenter; mStatusBar.mKeyguardIndicationController = mKeyguardIndicationController; mStatusBar.mBarService = mBarService; mStatusBar.mStackScroller = mStackScroller; mStatusBar.startKeyguard(); mInitController.executePostInitTasks(); - notificationLogger.setUpWithContainer(mStackScroller); + notificationLogger.setUpWithContainer(mNotificationListContainer); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java index eca48c8c2ee1..23fa6fd5e4ca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java @@ -29,6 +29,7 @@ import android.testing.TestableLooper; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.power.EnhancedEstimates; import org.junit.Assert; @@ -44,17 +45,21 @@ import org.mockito.MockitoAnnotations; @TestableLooper.RunWithLooper public class BatteryControllerTest extends SysuiTestCase { - @Mock - private PowerManager mPowerManager; - @Mock - private BroadcastDispatcher mBroadcastDispatcher; + @Mock private PowerManager mPowerManager; + @Mock private BroadcastDispatcher mBroadcastDispatcher; + @Mock private DemoModeController mDemoModeController; private BatteryControllerImpl mBatteryController; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mBatteryController = new BatteryControllerImpl(getContext(), mock(EnhancedEstimates.class), - mPowerManager, mBroadcastDispatcher, new Handler(), new Handler()); + mBatteryController = new BatteryControllerImpl(getContext(), + mock(EnhancedEstimates.class), + mPowerManager, + mBroadcastDispatcher, + mDemoModeController, + new Handler(), + new Handler()); mBatteryController.init(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index aef454fc1374..7db1b836f428 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -66,6 +66,7 @@ import com.android.settingslib.net.DataUsageController; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; import com.android.systemui.statusbar.policy.NetworkController.IconState; import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; @@ -113,6 +114,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { protected DeviceProvisionedController mMockProvisionController; protected DeviceProvisionedListener mUserCallback; protected Instrumentation mInstrumentation; + protected DemoModeController mDemoModeController; protected int mSubId; @@ -146,6 +148,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { res.addOverride(R.string.cell_data_off_content_description, NO_DATA_STRING); res.addOverride(R.string.not_default_data_content_description, NOT_DEFAULT_DATA_STRING); + mDemoModeController = mock(DemoModeController.class); mMockWm = mock(WifiManager.class); mMockTm = mock(TelephonyManager.class); mMockSm = mock(SubscriptionManager.class); @@ -200,10 +203,21 @@ public class NetworkControllerBaseTest extends SysuiTestCase { return null; }).when(mMockProvisionController).addCallback(any()); - mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, - mMockNsm, mMockSm, mConfig, TestableLooper.get(this).getLooper(), mCallbackHandler, - mock(AccessPointControllerImpl.class), mock(DataUsageController.class), - mMockSubDefaults, mMockProvisionController, mMockBd); + mNetworkController = new NetworkControllerImpl(mContext, + mMockCm, + mMockTm, + mMockWm, + mMockNsm, + mMockSm, + mConfig, + TestableLooper.get(this).getLooper(), + mCallbackHandler, + mock(AccessPointControllerImpl.class), + mock(DataUsageController.class), + mMockSubDefaults, + mMockProvisionController, + mMockBd, + mDemoModeController); setupNetworkController(); // Trigger blank callbacks to always get the current state (some tests don't trigger @@ -254,7 +268,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { mConfig, TestableLooper.get(this).getLooper(), mCallbackHandler, mock(AccessPointControllerImpl.class), mock(DataUsageController.class), mMockSubDefaults, - mock(DeviceProvisionedController.class), mMockBd); + mock(DeviceProvisionedController.class), mMockBd, mDemoModeController); setupNetworkController(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java index 6fffcff41a4f..d8aa29e9f766 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java @@ -106,7 +106,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { mMockNsm, mMockSm, mConfig, Looper.getMainLooper(), mCallbackHandler, mock(AccessPointControllerImpl.class), mock(DataUsageController.class), mMockSubDefaults, - mock(DeviceProvisionedController.class), mMockBd); + mock(DeviceProvisionedController.class), mMockBd, mDemoModeController); setupNetworkController(); setupDefaultSignal(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java index 3b2743775721..61f71b758d80 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java @@ -63,7 +63,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockNsm, mMockSm, mConfig, Looper.getMainLooper(), mCallbackHandler, mock(AccessPointControllerImpl.class), mock(DataUsageController.class), - mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd); + mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd, + mDemoModeController); setupNetworkController(); verifyLastMobileDataIndicators(false, -1, 0); @@ -81,7 +82,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockNsm, mMockSm, mConfig, Looper.getMainLooper(), mCallbackHandler, mock(AccessPointControllerImpl.class), mock(DataUsageController.class), - mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd); + mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd, + mDemoModeController); mNetworkController.registerListeners(); // Wait for the main looper to execute the previous command @@ -147,7 +149,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockNsm, mMockSm, mConfig, Looper.getMainLooper(), mCallbackHandler, mock(AccessPointControllerImpl.class), mock(DataUsageController.class), - mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd); + mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd, + mDemoModeController); setupNetworkController(); // No Subscriptions. diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index 9bb01ae5df1d..64be2d9a5599 100644 --- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -50,7 +50,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; -import com.android.testutils.HandlerUtilsKt; +import com.android.testutils.HandlerUtils; import com.android.testutils.TapPacketReader; import org.junit.After; @@ -366,7 +366,7 @@ public class EthernetTetheringTest { private TapPacketReader makePacketReader(FileDescriptor fd, int mtu) { final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu); mHandler.post(() -> reader.start()); - HandlerUtilsKt.waitForIdle(mHandler, TIMEOUT_MS); + HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS); return reader; } diff --git a/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java b/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java index 1499f3be224e..91c7771cc7fe 100644 --- a/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java +++ b/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java @@ -27,7 +27,7 @@ import android.net.TetheringRequestParcel; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.testutils.MiscAssertsKt; +import com.android.testutils.MiscAsserts; import org.junit.Before; import org.junit.Test; @@ -82,6 +82,6 @@ public class TetheringUtilsTest { request.showProvisioningUi = false; assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); - MiscAssertsKt.assertFieldCountEquals(5, TetheringRequestParcel.class); + MiscAsserts.assertFieldCountEquals(5, TetheringRequestParcel.class); } } diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java index b291438937c7..ce52ae22ece8 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java @@ -30,8 +30,8 @@ import static com.android.networkstack.tethering.OffloadController.StatsType.STA import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID; import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; -import static com.android.testutils.MiscAssertsKt.assertContainsAll; -import static com.android.testutils.MiscAssertsKt.assertThrows; +import static com.android.testutils.MiscAsserts.assertContainsAll; +import static com.android.testutils.MiscAsserts.assertThrows; import static com.android.testutils.NetworkStatsUtilsKt.assertNetworkStatsEquals; import static junit.framework.Assert.assertNotNull; diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index 46fe5cf093fd..1fe3840b51a8 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -143,7 +143,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.StateMachine; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; -import com.android.testutils.MiscAssertsKt; +import com.android.testutils.MiscAsserts; import org.junit.After; import org.junit.AfterClass; @@ -1360,7 +1360,7 @@ public class TetheringTest { assertEquals(0, parcel.localOnlyList.length); assertEquals(0, parcel.erroredIfaceList.length); assertEquals(0, parcel.lastErrorList.length); - MiscAssertsKt.assertFieldCountEquals(5, TetherStatesParcel.class); + MiscAsserts.assertFieldCountEquals(5, TetherStatesParcel.class); } @Test diff --git a/services/Android.bp b/services/Android.bp index ef52c2aff002..b348b91a1bd7 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -156,14 +156,10 @@ droidstubs { java_library { name: "android_system_server_stubs_current", - defaults: ["android_stubs_dists_default"], srcs: [":services-stubs.sources"], installable: false, static_libs: ["android_module_lib_stubs_current"], sdk_version: "none", system_modules: "none", java_version: "1.8", - dist: { - dir: "apistubs/android/system-server", - }, } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 833aeecc4c47..c41f4007aa59 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1530,12 +1530,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub int serviceCount = userState.mBoundServices.size(); for (int i = 0; i < serviceCount; i++) { AccessibilityServiceConnection service = userState.mBoundServices.get(i); - relevantEventTypes |= isClientInPackageWhitelist(service.getServiceInfo(), client) + relevantEventTypes |= isClientInPackageAllowlist(service.getServiceInfo(), client) ? service.getRelevantEventTypes() : 0; } - relevantEventTypes |= isClientInPackageWhitelist( + relevantEventTypes |= isClientInPackageAllowlist( mUiAutomationManager.getServiceInfo(), client) ? mUiAutomationManager.getRelevantEventTypes() : 0; @@ -1571,7 +1571,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private static boolean isClientInPackageWhitelist( + private static boolean isClientInPackageAllowlist( @Nullable AccessibilityServiceInfo serviceInfo, Client client) { if (serviceInfo == null) return false; @@ -1590,7 +1590,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub Slog.d(LOG_TAG, "Dropping events: " + Arrays.toString(clientPackages) + " -> " + serviceInfo.getComponentName().flattenToShortString() - + " due to not being in package whitelist " + + " due to not being in package allowlist " + Arrays.toString(serviceInfo.packageNames)); } } @@ -1991,9 +1991,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private void updateLegacyCapabilitiesLocked(AccessibilityUserState userState) { - // Up to JB-MR1 we had a white list with services that can enable touch + // Up to JB-MR1 we had a allowlist with services that can enable touch // exploration. When a service is first started we show a dialog to the - // use to get a permission to white list the service. + // use to get a permission to allowlist the service. final int installedServiceCount = userState.mInstalledServices.size(); for (int i = 0; i < installedServiceCount; i++) { AccessibilityServiceInfo serviceInfo = userState.mInstalledServices.get(i); @@ -2261,9 +2261,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } if (service.getServiceInfo().getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1) { - // Up to JB-MR1 we had a white list with services that can enable touch + // Up to JB-MR1 we had a allowlist with services that can enable touch // exploration. When a service is first started we show a dialog to the - // use to get a permission to white list the service. + // use to get a permission to allowlist the service. if (userState.mTouchExplorationGrantedServices.contains(service.mComponentName)) { return true; } else if (mEnableTouchExplorationDialog == null diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index c45da8668087..a2d58c8019fc 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -631,7 +631,7 @@ public class TouchExplorer extends BaseEventStreamTransformation // Two pointers moving in the same direction within // a given distance perform a drag. mState.startDragging(); - adjustEventLocationForDrag(event); + computeDraggingPointerIdIfNeeded(event); pointerIdBits = 1 << mDraggingPointerId; event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags()); mDispatcher.sendMotionEvent( @@ -794,7 +794,7 @@ public class TouchExplorer extends BaseEventStreamTransformation case 2: if (isDraggingGesture(event)) { // If still dragging send a drag event. - adjustEventLocationForDrag(event); + computeDraggingPointerIdIfNeeded(event); mDispatcher.sendMotionEvent( event, ACTION_MOVE, rawEvent, pointerIdBits, policyFlags); } else { @@ -959,44 +959,27 @@ public class TouchExplorer extends BaseEventStreamTransformation } /** - * Adjust the location of an injected event when performing a drag. The location will be the - * location of the finger closest to an edge of the screen. + * Computes {@link #mDraggingPointerId} if it is invalid. The pointer will be the finger + * closet to an edge of the screen. */ - private void adjustEventLocationForDrag(MotionEvent event) { + private void computeDraggingPointerIdIfNeeded(MotionEvent event) { + if (mDraggingPointerId != INVALID_POINTER_ID) { + // If we have a valid pointer ID, we should be good + final int pointerIndex = event.findPointerIndex(mDraggingPointerId); + if (event.findPointerIndex(pointerIndex) >= 0) { + return; + } + } + // Use the pointer that is closest to its closest edge. final float firstPtrX = event.getX(0); final float firstPtrY = event.getY(0); final int firstPtrId = event.getPointerId(0); final float secondPtrX = event.getX(1); final float secondPtrY = event.getY(1); final int secondPtrId = event.getPointerId(1); - float draggingX = firstPtrX; - float draggingY = firstPtrY; - if (mDraggingPointerId != INVALID_POINTER_ID) { - // Just use the coordinates of the dragging pointer. - int pointerIndex = event.findPointerIndex(mDraggingPointerId); - if (pointerIndex >= 0) { - draggingX = event.getX(pointerIndex); - draggingY = event.getY(pointerIndex); - } else { - // We've lost track of the dragging pointer. Try to recover by invalidating it. - // We'll the drop into the code below to choose a new one. - mDraggingPointerId = INVALID_POINTER_ID; - } - } - // Not quite an else, since the above code can invalidate the pointer - if (mDraggingPointerId == INVALID_POINTER_ID) { - // The goal is to use the coordinates of the finger that is closest to its closest edge. - if (getDistanceToClosestEdge(firstPtrX, firstPtrY) - < getDistanceToClosestEdge(secondPtrX, secondPtrY)) { - // X and Y initialized to firstPtrX and Y was right - mDraggingPointerId = firstPtrId; - } else { - draggingX = secondPtrX; - draggingY = secondPtrY; - mDraggingPointerId = secondPtrId; - } - } - event.setLocation(draggingX, draggingY); + mDraggingPointerId = (getDistanceToClosestEdge(firstPtrX, firstPtrY) + < getDistanceToClosestEdge(secondPtrX, secondPtrY)) + ? firstPtrId : secondPtrId; } private float getDistanceToClosestEdge(float x, float y) { diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 663fd6259fd4..ad85784ca066 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -360,7 +360,7 @@ public final class AutofillManagerService @Override // from SystemService public boolean isUserSupported(TargetUser user) { - return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile(); + return user.isFull() || user.isManagedProfile(); } @Override // from SystemService diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 29235dd1ee72..e68c07ed73f7 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -441,6 +441,7 @@ public class UserBackupManagerService { private long mAncestralToken = 0; private long mCurrentToken = 0; @Nullable private File mAncestralSerialNumberFile; + @OperationType private volatile long mAncestralOperationType; private final ContentObserver mSetupObserver; private final BroadcastReceiver mRunInitReceiver; @@ -881,6 +882,10 @@ public class UserBackupManagerService { mAncestralToken = ancestralToken; } + public void setAncestralOperationType(@OperationType int operationType) { + mAncestralOperationType = operationType; + } + public long getCurrentToken() { return mCurrentToken; } @@ -1808,6 +1813,16 @@ public class UserBackupManagerService { } } + private BackupEligibilityRules getEligibilityRulesForRestoreAtInstall(long restoreToken) { + if (mAncestralOperationType == OperationType.MIGRATION && restoreToken == mAncestralToken) { + return getEligibilityRulesForOperation(OperationType.MIGRATION); + } else { + // If we're not using the ancestral data set, it means we're restoring from a backup + // that happened on this device. + return mScheduledBackupEligibility; + } + } + /** * Get the restore-set token for the best-available restore set for this {@code packageName}: * the active set if possible, else the ancestral one. Returns zero if none available. @@ -3976,7 +3991,7 @@ public class UserBackupManagerService { packageName, token, listener, - mScheduledBackupEligibility); + getEligibilityRulesForRestoreAtInstall(restoreSet)); mBackupHandler.sendMessage(msg); } catch (Exception e) { // Calling into the transport broke; back off and proceed with the installation. diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java index 622067999f27..b9625397d237 100644 --- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java +++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java @@ -27,6 +27,7 @@ import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERA import android.app.ApplicationThreadConstants; import android.app.IBackupAgent; +import android.app.backup.BackupManager; import android.app.backup.FullBackup; import android.app.backup.IBackupManagerMonitor; import android.app.backup.IFullBackupRestoreObserver; @@ -633,7 +634,11 @@ public class FullRestoreEngine extends RestoreEngine { setRunning(false); } - private static boolean isRestorableFile(FileMetadata info) { + private boolean isRestorableFile(FileMetadata info) { + if (mBackupEligibilityRules.getOperationType() == BackupManager.OperationType.MIGRATION) { + // Everything is eligible for device-to-device migration. + return true; + } if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) { if (MORE_DEBUG) { Slog.i(TAG, "Dropping cache file path " + info.path); diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index 7baf55992770..abf11bd542a1 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -1136,6 +1136,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { if (mIsSystemRestore && mPmAgent != null) { backupManagerService.setAncestralPackages(mPmAgent.getRestoredPackages()); backupManagerService.setAncestralToken(mToken); + backupManagerService.setAncestralOperationType( + mBackupEligibilityRules.getOperationType()); backupManagerService.writeRestoreTokens(); } diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java index d6598975a647..73ba1f19c092 100644 --- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java +++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java @@ -85,12 +85,15 @@ public class BackupEligibilityRules { * <li>they run as a system-level uid but do not supply their own backup agent * <li>it is the special shared-storage backup package used for 'adb backup' * </ol> + * + * However, the above eligibility rules are ignored for non-system apps in in case of + * device-to-device migration, see {@link OperationType}. */ @VisibleForTesting public boolean appIsEligibleForBackup(ApplicationInfo app) { - // 1. their manifest states android:allowBackup="false" - boolean appAllowsBackup = (app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0; - if (!appAllowsBackup && !forceFullBackup(app.uid, mOperationType)) { + // 1. their manifest states android:allowBackup="false" and this is not a device-to-device + // migration + if (!isAppBackupAllowed(app)) { return false; } @@ -123,6 +126,23 @@ public class BackupEligibilityRules { } /** + * Check if this app allows backup. Apps can opt out of backup by stating + * android:allowBackup="false" in their manifest. However, this flag is ignored for non-system + * apps during device-to-device migrations, see {@link OperationType}. + * + * @param app The app under check. + * @return boolean indicating whether backup is allowed. + */ + public boolean isAppBackupAllowed(ApplicationInfo app) { + if (mOperationType == OperationType.MIGRATION && !UserHandle.isCore(app.uid)) { + // Backup / restore of all apps is force allowed during device-to-device migration. + return true; + } + + return (app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0; + } + + /** * Returns whether an app is eligible for backup at runtime. That is, the app has to: * <ol> * <li>Return true for {@link #appIsEligibleForBackup(ApplicationInfo, int)} diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java index bf8e9c8512ae..3789fa14e87b 100644 --- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java +++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java @@ -402,7 +402,7 @@ public class TarBackupReader { info.packageName, PackageManager.GET_SIGNING_CERTIFICATES, userId); // Fall through to IGNORE if the app explicitly disallows backup final int flags = pkgInfo.applicationInfo.flags; - if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { + if (eligibilityRules.isAppBackupAllowed(pkgInfo.applicationInfo)) { // Restore system-uid-space packages only if they have // defined a custom backup agent if (!UserHandle.isCore(pkgInfo.applicationInfo.uid) diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index ea94ad0b3c20..e742015916e7 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -219,7 +219,7 @@ public final class ContentCaptureManagerService extends @Override // from SystemService public boolean isUserSupported(TargetUser user) { - return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile(); + return user.isFull() || user.isManagedProfile(); } @Override // from SystemService diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/android/os/UserManagerInternal.java index fbe8c04bd59c..61e8128bd510 100644 --- a/services/core/java/android/os/UserManagerInternal.java +++ b/services/core/java/android/os/UserManagerInternal.java @@ -262,8 +262,7 @@ public abstract class UserManagerInternal { public abstract boolean hasUserRestriction(String restriction, int userId); /** - * Gets an {@link UserInfo} for the given {@code userId}, or {@code null} if not - * found. + * Gets a {@link UserInfo} for the given {@code userId}, or {@code null} if not found. */ public abstract @Nullable UserInfo getUserInfo(@UserIdInt int userId); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index ef62a991f323..bd590d317910 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -4966,7 +4966,7 @@ public class ConnectivityService extends IConnectivityManager.Stub Slog.w(TAG, "User " + userId + " has no Vpn configuration"); return null; } - return vpn.getLockdownWhitelist(); + return vpn.getLockdownAllowlist(); } } diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 6402e07bddc3..b2f0c8376db1 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -1477,7 +1477,7 @@ public class IpSecService extends IIpSecService.Stub { } /** - * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an + * Checks an IpSecConfig parcel to ensure that the contents are valid and throws an * IllegalArgumentException if they are not. */ private void checkIpSecConfig(IpSecConfig config) { diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 97f3b373f63e..ee794badad88 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -20,14 +20,14 @@ import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.OBSERVE_NETWORK_POLICY; import static android.Manifest.permission.SHUTDOWN; -import static android.net.INetd.FIREWALL_BLACKLIST; +import static android.net.INetd.FIREWALL_ALLOWLIST; import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; import static android.net.INetd.FIREWALL_CHAIN_NONE; import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; import static android.net.INetd.FIREWALL_CHAIN_STANDBY; +import static android.net.INetd.FIREWALL_DENYLIST; import static android.net.INetd.FIREWALL_RULE_ALLOW; import static android.net.INetd.FIREWALL_RULE_DENY; -import static android.net.INetd.FIREWALL_WHITELIST; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; @@ -1575,7 +1575,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { enforceSystemUid(); try { mNetdService.firewallSetFirewallType( - enabled ? INetd.FIREWALL_WHITELIST : INetd.FIREWALL_BLACKLIST); + enabled ? INetd.FIREWALL_ALLOWLIST : INetd.FIREWALL_DENYLIST); mFirewallEnabled = enabled; } catch (RemoteException | ServiceSpecificException e) { throw new IllegalStateException(e); @@ -1608,7 +1608,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { int numUids = 0; if (DBG) Slog.d(TAG, "Closing sockets after enabling chain " + chainName); - if (getFirewallType(chain) == FIREWALL_WHITELIST) { + if (getFirewallType(chain) == FIREWALL_ALLOWLIST) { // Close all sockets on all non-system UIDs... ranges = new UidRangeParcel[] { // TODO: is there a better way of finding all existing users? If so, we could @@ -1714,13 +1714,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub { private int getFirewallType(int chain) { switch (chain) { case FIREWALL_CHAIN_STANDBY: - return FIREWALL_BLACKLIST; + return FIREWALL_DENYLIST; case FIREWALL_CHAIN_DOZABLE: - return FIREWALL_WHITELIST; + return FIREWALL_ALLOWLIST; case FIREWALL_CHAIN_POWERSAVE: - return FIREWALL_WHITELIST; + return FIREWALL_ALLOWLIST; default: - return isFirewallEnabled() ? FIREWALL_WHITELIST : FIREWALL_BLACKLIST; + return isFirewallEnabled() ? FIREWALL_ALLOWLIST : FIREWALL_DENYLIST; } } @@ -1822,7 +1822,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { private @NonNull String getFirewallRuleName(int chain, int rule) { String ruleName; - if (getFirewallType(chain) == FIREWALL_WHITELIST) { + if (getFirewallType(chain) == FIREWALL_ALLOWLIST) { if (rule == FIREWALL_RULE_ALLOW) { ruleName = "allow"; } else { @@ -1856,7 +1856,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { private int getFirewallRuleType(int chain, int rule) { if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) { - return getFirewallType(chain) == FIREWALL_WHITELIST + return getFirewallType(chain) == FIREWALL_ALLOWLIST ? INetd.FIREWALL_RULE_DENY : INetd.FIREWALL_RULE_ALLOW; } return rule; diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java index 0038dc2e8da0..b78b5d945a08 100644 --- a/services/core/java/com/android/server/ServiceWatcher.java +++ b/services/core/java/com/android/server/ServiceWatcher.java @@ -238,24 +238,13 @@ public class ServiceWatcher implements ServiceConnection { new PackageMonitor() { @Override - public void onPackageUpdateFinished(String packageName, int uid) { - ServiceWatcher.this.onPackageChanged(packageName); - } - - @Override - public void onPackageAdded(String packageName, int uid) { - ServiceWatcher.this.onPackageChanged(packageName); - } - - @Override - public void onPackageRemoved(String packageName, int uid) { - ServiceWatcher.this.onPackageChanged(packageName); + public boolean onPackageChanged(String packageName, int uid, String[] components) { + return true; } @Override - public boolean onPackageChanged(String packageName, int uid, String[] components) { - ServiceWatcher.this.onPackageChanged(packageName); - return super.onPackageChanged(packageName, uid, components); + public void onSomePackagesChanged() { + onBestServiceChanged(false); } }.register(mContext, UserHandle.ALL, true, mHandler); @@ -320,7 +309,7 @@ public class ServiceWatcher implements ServiceConnection { if (!mTargetService.equals(ServiceInfo.NONE)) { if (D) { - Log.i(TAG, "[" + mIntent.getAction() + "] unbinding from " + mTargetService); + Log.d(TAG, "[" + mIntent.getAction() + "] unbinding from " + mTargetService); } mContext.unbindService(this); @@ -335,9 +324,7 @@ public class ServiceWatcher implements ServiceConnection { Preconditions.checkState(mTargetService.component != null); - if (D) { - Log.i(TAG, getLogPrefix() + " binding to " + mTargetService); - } + Log.i(TAG, getLogPrefix() + " binding to " + mTargetService); Intent bindIntent = new Intent(mIntent).setComponent(mTargetService.component); if (!mContext.bindServiceAsUser(bindIntent, this, @@ -355,7 +342,7 @@ public class ServiceWatcher implements ServiceConnection { Preconditions.checkState(mBinder == null); if (D) { - Log.i(TAG, getLogPrefix() + " connected to " + component.toShortString()); + Log.d(TAG, getLogPrefix() + " connected to " + component.toShortString()); } mBinder = binder; @@ -379,7 +366,7 @@ public class ServiceWatcher implements ServiceConnection { } if (D) { - Log.i(TAG, getLogPrefix() + " disconnected from " + component.toShortString()); + Log.d(TAG, getLogPrefix() + " disconnected from " + component.toShortString()); } mBinder = null; @@ -392,13 +379,16 @@ public class ServiceWatcher implements ServiceConnection { public final void onBindingDied(ComponentName component) { Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); - if (D) { - Log.i(TAG, getLogPrefix() + " " + component.toShortString() + " died"); - } + Log.i(TAG, getLogPrefix() + " " + component.toShortString() + " died"); onBestServiceChanged(true); } + @Override + public final void onNullBinding(ComponentName component) { + Log.e(TAG, getLogPrefix() + " " + component.toShortString() + " has null binding"); + } + void onUserSwitched(@UserIdInt int userId) { mCurrentUserId = userId; onBestServiceChanged(false); @@ -410,11 +400,6 @@ public class ServiceWatcher implements ServiceConnection { } } - void onPackageChanged(String packageName) { - // force a rebind if the changed package was the currently connected package - onBestServiceChanged(packageName.equals(mTargetService.getPackageName())); - } - /** * Runs the given function asynchronously if and only if currently connected. Suppresses any * RemoteException thrown during execution. diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index eca6036ebf8e..b72985cc8f2c 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -1141,13 +1141,6 @@ class StorageManagerService extends IStorageManager.Stub } private void completeUnlockUser(int userId) { - // If user 0 has completed unlock, perform a one-time migration of legacy obb data - // to its new location. This may take time depending on the size of the data to be copied - // so it's done on the StorageManager handler thread. - if (userId == 0) { - mPmInternal.migrateLegacyObbData(); - } - onKeyguardStateChanged(false); // Record user as started so newly mounted volumes kick off events @@ -1540,9 +1533,21 @@ class StorageManagerService extends IStorageManager.Stub mFuseMountedUser.remove(vol.getMountUserId()); } else if (mVoldAppDataIsolationEnabled){ final int userId = vol.getMountUserId(); - mFuseMountedUser.add(userId); // Async remount app storage so it won't block the main thread. new Thread(() -> { + + // If user 0 has completed unlock, perform a one-time migration of legacy + // obb data to its new location. This may take time depending on the size of + // the data to be copied so it's done on the StorageManager worker thread. + // This needs to be finished before start mounting obb directories. + if (userId == 0) { + mPmInternal.migrateLegacyObbData(); + } + + // Add fuse mounted user after migration to prevent ProcessList tries to + // create obb directory before migration is done. + mFuseMountedUser.add(userId); + Map<Integer, String> pidPkgMap = null; // getProcessesWithPendingBindMounts() could fail when a new app process is // starting and it's not planning to mount storage dirs in zygote, but it's @@ -3282,7 +3287,7 @@ class StorageManagerService extends IStorageManager.Stub final UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class); - for (UserInfo user : um.getUsers(false /* includeDying */)) { + for (UserInfo user : um.getUsers()) { final int flags; if (umInternal.isUserUnlockingOrUnlocked(user.id)) { flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java index 1496e926b95b..84d01ec3598d 100644 --- a/services/core/java/com/android/server/SystemService.java +++ b/services/core/java/com/android/server/SystemService.java @@ -23,6 +23,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.SystemApi.Client; +import android.annotation.UserIdInt; import android.app.ActivityThread; import android.content.Context; import android.content.pm.UserInfo; @@ -131,45 +132,89 @@ public abstract class SystemService { */ @SystemApi(client = Client.SYSTEM_SERVER) public static final class TargetUser { - @NonNull - private final UserInfo mUserInfo; + + // NOTE: attributes below must be immutable while ther user is running (i.e., from the + // moment it's started until after it's shutdown). + private final @UserIdInt int mUserId; + private final boolean mFull; + private final boolean mManagedProfile; + private final boolean mPreCreated; /** @hide */ public TargetUser(@NonNull UserInfo userInfo) { - mUserInfo = userInfo; + mUserId = userInfo.id; + mFull = userInfo.isFull(); + mManagedProfile = userInfo.isManagedProfile(); + mPreCreated = userInfo.preCreated; } /** - * @return The information about the user. <b>NOTE: </b> this is a "live" object - * referenced by {@link UserManagerService} and hence should not be modified. + * Checks if the target user is {@link UserInfo#isFull() full}. * * @hide */ - @NonNull - public UserInfo getUserInfo() { - return mUserInfo; + public boolean isFull() { + return mFull; + } + + /** + * Checks if the target user is a managed profile. + * + * @hide + */ + public boolean isManagedProfile() { + return mManagedProfile; + } + + /** + * Checks if the target user is a pre-created user. + * + * @hide + */ + public boolean isPreCreated() { + return mPreCreated; } /** - * @return the target {@link UserHandle}. + * Gets the target user's {@link UserHandle}. */ @NonNull public UserHandle getUserHandle() { - return mUserInfo.getUserHandle(); + return UserHandle.of(mUserId); } /** - * @return the integer user id + * Gets the target user's id. * * @hide */ - public int getUserIdentifier() { - return mUserInfo.id; + public @UserIdInt int getUserIdentifier() { + return mUserId; } @Override public String toString() { - return Integer.toString(getUserIdentifier()); + return Integer.toString(mUserId); + } + + /** + * @hide + */ + public void dump(@NonNull StringBuilder builder) { + builder.append(getUserIdentifier()); + + if (!isFull() && !isManagedProfile()) return; + + builder.append('('); + boolean addComma = false; + if (isFull()) { + builder.append("full"); + } + if (isManagedProfile()) { + if (addComma) builder.append(','); + builder.append("mp"); + } + builder.append(')'); } } diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index 74bb7d7e90f1..34e63705d781 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -27,7 +27,10 @@ import android.os.UserHandle; import android.os.UserManagerInternal; import android.util.ArrayMap; import android.util.Slog; +import android.util.SparseArray; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; import com.android.server.SystemService.TargetUser; import com.android.server.utils.TimingsTraceAndSlog; @@ -44,8 +47,8 @@ import java.util.ArrayList; * * {@hide} */ -public class SystemServiceManager { - private static final String TAG = "SystemServiceManager"; +public final class SystemServiceManager { + private static final String TAG = SystemServiceManager.class.getSimpleName(); private static final boolean DEBUG = false; private static final int SERVICE_CALL_WARN_TIME_MS = 50; @@ -74,6 +77,13 @@ public class SystemServiceManager { private UserManagerInternal mUserManagerInternal; + /** + * Map of started {@link TargetUser TargetUsers} by user id; users are added on start and + * removed after they're completely shut down. + */ + @GuardedBy("mTargetUsers") + private final SparseArray<TargetUser> mTargetUsers = new SparseArray<>(); + SystemServiceManager(Context context) { mContext = context; } @@ -240,75 +250,85 @@ public class SystemServiceManager { mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); } - private @NonNull UserInfo getUserInfo(@UserIdInt int userHandle) { - if (mUserManagerInternal == null) { - throw new IllegalStateException("mUserManagerInternal not set yet"); + private @NonNull TargetUser getTargetUser(@UserIdInt int userId) { + final TargetUser targetUser; + synchronized (mTargetUsers) { + targetUser = mTargetUsers.get(userId); } - final UserInfo userInfo = mUserManagerInternal.getUserInfo(userHandle); - if (userInfo == null) { - throw new IllegalStateException("No UserInfo for " + userHandle); - } - return userInfo; + Preconditions.checkState(targetUser != null, "No TargetUser for " + userId); + return targetUser; } /** * Starts the given user. */ - public void startUser(final @NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle) { - onUser(t, START, userHandle); + public void startUser(@NonNull TimingsTraceAndSlog t, @UserIdInt int userId) { + // Create cached TargetUser + final UserInfo userInfo = mUserManagerInternal.getUserInfo(userId); + Preconditions.checkState(userInfo != null, "No UserInfo for " + userId); + synchronized (mTargetUsers) { + mTargetUsers.put(userId, new TargetUser(userInfo)); + } + + onUser(t, START, userId); } /** * Unlocks the given user. */ - public void unlockUser(final @UserIdInt int userHandle) { - onUser(UNLOCKING, userHandle); + public void unlockUser(@UserIdInt int userId) { + onUser(UNLOCKING, userId); } /** * Called after the user was unlocked. */ - public void onUserUnlocked(final @UserIdInt int userHandle) { - onUser(UNLOCKED, userHandle); + public void onUserUnlocked(@UserIdInt int userId) { + onUser(UNLOCKED, userId); } /** * Switches to the given user. */ - public void switchUser(final @UserIdInt int from, final @UserIdInt int to) { + public void switchUser(@UserIdInt int from, @UserIdInt int to) { onUser(TimingsTraceAndSlog.newAsyncLog(), SWITCH, to, from); } /** * Stops the given user. */ - public void stopUser(final @UserIdInt int userHandle) { - onUser(STOP, userHandle); + public void stopUser(@UserIdInt int userId) { + onUser(STOP, userId); } /** * Cleans up the given user. */ - public void cleanupUser(final @UserIdInt int userHandle) { - onUser(CLEANUP, userHandle); + public void cleanupUser(@UserIdInt int userId) { + onUser(CLEANUP, userId); + + // Remove cached TargetUser + synchronized (mTargetUsers) { + mTargetUsers.remove(userId); + } } - private void onUser(@NonNull String onWhat, @UserIdInt int userHandle) { - onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, userHandle); + private void onUser(@NonNull String onWhat, @UserIdInt int userId) { + onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, userId); } private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat, - @UserIdInt int userHandle) { - onUser(t, onWhat, userHandle, UserHandle.USER_NULL); + @UserIdInt int userId) { + onUser(t, onWhat, userId, UserHandle.USER_NULL); } private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat, @UserIdInt int curUserId, @UserIdInt int prevUserId) { t.traceBegin("ssm." + onWhat + "User-" + curUserId); Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId); - final TargetUser curUser = new TargetUser(getUserInfo(curUserId)); + final TargetUser curUser = getTargetUser(curUserId); final TargetUser prevUser = prevUserId == UserHandle.USER_NULL ? null - : new TargetUser(getUserInfo(prevUserId)); + : getTargetUser(prevUserId); final int serviceLen = mServices.size(); for (int i = 0; i < serviceLen; i++) { final SystemService service = mServices.get(i); @@ -437,7 +457,7 @@ public class SystemServiceManager { */ public void dump() { StringBuilder builder = new StringBuilder(); - builder.append("Current phase: ").append(mCurrentPhase).append("\n"); + builder.append("Current phase: ").append(mCurrentPhase).append('\n'); builder.append("Services:\n"); final int startedLen = mServices.size(); for (int i = 0; i < startedLen; i++) { @@ -447,6 +467,14 @@ public class SystemServiceManager { .append("\n"); } + builder.append("Target users: "); + final int targetUsersSize = mTargetUsers.size(); + for (int i = 0; i < targetUsersSize; i++) { + mTargetUsers.valueAt(i).dump(builder); + if (i != targetUsersSize - 1) builder.append(','); + } + builder.append('\n'); + Slog.e(TAG, builder.toString()); } } diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 59ac09ca2f3d..96d973ec5272 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -114,9 +114,6 @@ public class VibratorService extends IVibratorService.Stub private static final VibrationAttributes DEFAULT_ATTRIBUTES = new VibrationAttributes.Builder().build(); - // If HAL supports callbacks set the timeout to ASYNC_TIMEOUT_MULTIPLIER * duration. - private static final long ASYNC_TIMEOUT_MULTIPLIER = 2; - // A mapping from the intensity adjustment to the scaling to apply, where the intensity // adjustment is defined as the delta between the default intensity level and the user selected // intensity level. It's important that we apply the scaling on the delta between the two so @@ -130,6 +127,7 @@ public class VibratorService extends IVibratorService.Stub private final int mPreviousVibrationsLimit; private final boolean mAllowPriorityVibrationsInLowPowerMode; private final List<Integer> mSupportedEffects; + private final List<Integer> mSupportedPrimitives; private final long mCapabilities; private final int mDefaultVibrationAmplitude; private final SparseArray<VibrationEffect> mFallbackEffects; @@ -187,8 +185,10 @@ public class VibratorService extends IVibratorService.Stub static native int[] vibratorGetSupportedEffects(long controllerPtr); - static native long vibratorPerformEffect(long effect, long strength, Vibration vibration, - boolean withCallback); + static native int[] vibratorGetSupportedPrimitives(long controllerPtr); + + static native long vibratorPerformEffect( + long controllerPtr, long effect, long strength, Vibration vibration); static native void vibratorPerformComposedEffect(long controllerPtr, VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration); @@ -400,6 +400,7 @@ public class VibratorService extends IVibratorService.Stub mNativeWrapper.vibratorOff(); mSupportedEffects = asList(mNativeWrapper.vibratorGetSupportedEffects()); + mSupportedPrimitives = asList(mNativeWrapper.vibratorGetSupportedPrimitives()); mCapabilities = mNativeWrapper.vibratorGetCapabilities(); mContext = context; @@ -650,8 +651,11 @@ public class VibratorService extends IVibratorService.Stub @Override // Binder call public boolean[] arePrimitivesSupported(int[] primitiveIds) { boolean[] supported = new boolean[primitiveIds.length]; - if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) { - Arrays.fill(supported, true); + if (!hasCapability(IVibrator.CAP_COMPOSE_EFFECTS) || mSupportedPrimitives == null) { + return supported; + } + for (int i = 0; i < primitiveIds.length; i++) { + supported[i] = mSupportedPrimitives.contains(primitiveIds[i]); } return supported; } @@ -898,19 +902,11 @@ public class VibratorService extends IVibratorService.Stub } } - private final Runnable mVibrationEndRunnable = new Runnable() { - @Override - public void run() { - onVibrationFinished(); - } - }; - @GuardedBy("mLock") private void doCancelVibrateLocked() { Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked"); try { - mH.removeCallbacks(mVibrationEndRunnable); if (mThread != null) { mThread.cancel(); mThread = null; @@ -958,13 +954,11 @@ public class VibratorService extends IVibratorService.Stub private void startVibrationInnerLocked(Vibration vib) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked"); try { - long timeout = 0; mCurrentVibration = vib; if (vib.effect instanceof VibrationEffect.OneShot) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect; doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib); - timeout = oneShot.getDuration() * ASYNC_TIMEOUT_MULTIPLIER; } else if (vib.effect instanceof VibrationEffect.Waveform) { // mThread better be null here. doCancelVibrate should always be // called before startNextVibrationLocked or startVibrationLocked. @@ -973,24 +967,13 @@ public class VibratorService extends IVibratorService.Stub mThread.start(); } else if (vib.effect instanceof VibrationEffect.Prebaked) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); - timeout = doVibratorPrebakedEffectLocked(vib); + doVibratorPrebakedEffectLocked(vib); } else if (vib.effect instanceof VibrationEffect.Composed) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); doVibratorComposedEffectLocked(vib); - // FIXME: We rely on the completion callback here, but I don't think we require that - // devices which support composition also support the completion callback. If we - // ever get a device that supports the former but not the latter, then we have no - // real way of knowing how long a given effect should last. - timeout = 10_000; } else { Slog.e(TAG, "Unknown vibration type, ignoring"); } - // Post extra runnable to ensure vibration will end even if the HAL or native controller - // never triggers the callback. - // TODO: Move ASYNC_TIMEOUT_MULTIPLIER here once native controller is fully integrated. - if (timeout > 0) { - mH.postDelayed(mVibrationEndRunnable, timeout); - } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); } @@ -1354,7 +1337,7 @@ public class VibratorService extends IVibratorService.Stub } @GuardedBy("mLock") - private long doVibratorPrebakedEffectLocked(Vibration vib) { + private void doVibratorPrebakedEffectLocked(Vibration vib) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorPrebakedEffectLocked"); try { final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect; @@ -1364,25 +1347,20 @@ public class VibratorService extends IVibratorService.Stub } // Input devices don't support prebaked effect, so skip trying it with them. if (!usingInputDeviceVibrators) { - long duration = mNativeWrapper.vibratorPerformEffect(prebaked.getId(), - prebaked.getEffectStrength(), vib, - hasCapability(IVibrator.CAP_PERFORM_CALLBACK)); - long timeout = duration; - if (hasCapability(IVibrator.CAP_PERFORM_CALLBACK)) { - timeout *= ASYNC_TIMEOUT_MULTIPLIER; - } - if (timeout > 0) { + long duration = mNativeWrapper.vibratorPerformEffect( + prebaked.getId(), prebaked.getEffectStrength(), vib); + if (duration > 0) { noteVibratorOnLocked(vib.uid, duration); - return timeout; + return; } } if (!prebaked.shouldFallback()) { - return 0; + return; } VibrationEffect effect = getFallbackEffect(prebaked.getId()); if (effect == null) { Slog.w(TAG, "Failed to play prebaked effect, no fallback"); - return 0; + return; } Vibration fallbackVib = new Vibration(vib.token, effect, vib.attrs, vib.uid, vib.opPkg, vib.reason + " (fallback)"); @@ -1390,7 +1368,7 @@ public class VibratorService extends IVibratorService.Stub linkVibration(fallbackVib); applyVibrationIntensityScalingLocked(fallbackVib, intensity); startVibrationInnerLocked(fallbackVib); - return 0; + return; } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); } @@ -1530,6 +1508,7 @@ public class VibratorService extends IVibratorService.Stub pw.println(" mNotificationIntensity=" + mNotificationIntensity); pw.println(" mRingIntensity=" + mRingIntensity); pw.println(" mSupportedEffects=" + mSupportedEffects); + pw.println(" mSupportedPrimitives=" + mSupportedPrimitives); pw.println(); pw.println(" Previous ring vibrations:"); for (VibrationInfo info : mPreviousRingVibrations) { @@ -1788,10 +1767,15 @@ public class VibratorService extends IVibratorService.Stub return VibratorService.vibratorGetSupportedEffects(mNativeControllerPtr); } + /** Returns all compose primitives supported by the device vibrator. */ + public int[] vibratorGetSupportedPrimitives() { + return VibratorService.vibratorGetSupportedPrimitives(mNativeControllerPtr); + } + /** Turns vibrator on to perform one of the supported effects. */ - public long vibratorPerformEffect(long effect, long strength, Vibration vibration, - boolean withCallback) { - return VibratorService.vibratorPerformEffect(effect, strength, vibration, withCallback); + public long vibratorPerformEffect(long effect, long strength, Vibration vibration) { + return VibratorService.vibratorPerformEffect( + mNativeControllerPtr, effect, strength, vibration); } /** Turns vibrator on to perform one of the supported composed effects. */ diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 3eb26de3a675..6328cb60cf73 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -493,6 +493,8 @@ public final class ActiveServices { } ServiceRecord r = res.record; + setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, r, + allowBackgroundActivityStarts); if (!mAm.mUserController.exists(r.userId)) { Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId); @@ -516,6 +518,21 @@ public final class ActiveServices { forcedStandby = true; } + if (fgRequired) { + if (!r.mAllowStartForeground) { + if (!r.mLoggedInfoAllowStartForeground) { + Slog.wtf(TAG, "Background started FGS " + r.mInfoAllowStartForeground); + r.mLoggedInfoAllowStartForeground = true; + } + if (mAm.mConstants.mFlagFgsStartRestrictionEnabled) { + Slog.w(TAG, "startForegroundService() not allowed due to " + + " mAllowStartForeground false: service " + + r.shortInstanceName); + forcedStandby = true; + } + } + } + // If this is a direct-to-foreground start, make sure it is allowed as per the app op. boolean forceSilentAbort = false; if (fgRequired) { @@ -688,18 +705,10 @@ public final class ActiveServices { "Not potential delay (user " + r.userId + " not started): " + r); } } - if (allowBackgroundActivityStarts) { r.allowBgActivityStartsOnServiceStart(); } ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); - - if (!r.mAllowWhileInUsePermissionInFgs) { - r.mAllowWhileInUsePermissionInFgs = - shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid, - callingUid, service, r, allowBackgroundActivityStarts); - } - return cmp; } @@ -1369,6 +1378,7 @@ public final class ActiveServices { + r.shortInstanceName); } } + boolean alreadyStartedOp = false; boolean stopProcStatsOp = false; if (r.fgRequired) { @@ -1415,6 +1425,24 @@ public final class ActiveServices { ignoreForeground = true; } + if (!ignoreForeground) { + if (!r.mAllowStartForeground) { + if (!r.mLoggedInfoAllowStartForeground) { + Slog.wtf(TAG, "Background started FGS " + + r.mInfoAllowStartForeground); + r.mLoggedInfoAllowStartForeground = true; + } + if (mAm.mConstants.mFlagFgsStartRestrictionEnabled) { + Slog.w(TAG, + "Service.startForeground() not allowed due to " + + "mAllowStartForeground false: service " + + r.shortInstanceName); + updateServiceForegroundLocked(r.app, true); + ignoreForeground = true; + } + } + } + // Apps under strict background restrictions simply don't get to have foreground // services, so now that we've enforced the startForegroundService() contract // we only do the machinery of making the service foreground when the app @@ -2067,12 +2095,7 @@ public final class ActiveServices { } } - if (!s.mAllowWhileInUsePermissionInFgs) { - s.mAllowWhileInUsePermissionInFgs = - shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, - callingPid, callingUid, - service, s, false); - } + setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, false); if (s.app != null) { if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { @@ -4840,21 +4863,48 @@ public final class ActiveServices { } /** - * Should allow while-in-use permissions in foreground service or not. - * while-in-use permissions in FGS started from background might be restricted. + * There are two FGS restrictions: + * In R, mAllowWhileInUsePermissionInFgs is to allow while-in-use permissions in foreground + * service or not. while-in-use permissions in FGS started from background might be restricted. + * In S, mAllowStartForeground is to allow FGS to startForeground or not. Service started + * from background may not become a FGS. * @param callingPackage caller app's package name. * @param callingUid caller app's uid. * @param intent intent to start/bind service. * @param r the service to start. * @return true if allow, false otherwise. */ - private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage, + private void setFgsRestrictionLocked(String callingPackage, int callingPid, int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) { - // Is the background FGS start restriction turned on? + // Check DeviceConfig flag. if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) { - return true; + r.mAllowWhileInUsePermissionInFgs = true; } + + if (!r.mAllowWhileInUsePermissionInFgs || !r.mAllowStartForeground) { + final boolean temp = shouldAllowFgsFeatureLocked(callingPackage, callingPid, + callingUid, intent, r, allowBackgroundActivityStarts); + if (!r.mAllowWhileInUsePermissionInFgs) { + r.mAllowWhileInUsePermissionInFgs = temp; + } + if (!r.mAllowStartForeground) { + r.mAllowStartForeground = temp; + } + } + } + + /** + * Should allow FGS feature or not. + * @param callingPackage caller app's package name. + * @param callingUid caller app's uid. + * @param intent intent to start/bind service. + * @param r the service to start. + * @return true if allow, false otherwise. + */ + private boolean shouldAllowFgsFeatureLocked(String callingPackage, + int callingPid, int callingUid, Intent intent, ServiceRecord r, + boolean allowBackgroundActivityStarts) { // Is the allow activity background start flag on? if (allowBackgroundActivityStarts) { return true; @@ -4894,10 +4944,11 @@ public final class ActiveServices { } // Is the calling UID at PROCESS_STATE_TOP or above? - final boolean isCallingUidTopApp = appIsTopLocked(callingUid); - if (isCallingUidTopApp) { + final int uidState = mAm.getUidState(callingUid); + if (uidState <= ActivityManager.PROCESS_STATE_TOP) { return true; } + // Does the calling UID have any visible activity? final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid); if (isCallingUidVisible) { @@ -4915,6 +4966,19 @@ public final class ActiveServices { if (isDeviceOwner) { return true; } + + final String info = + "[callingPackage: " + callingPackage + + "; callingUid: " + callingUid + + "; uidState: " + ProcessList.makeProcStateString(uidState) + + "; intent: " + intent + + "; targetSdkVersion:" + r.appInfo.targetSdkVersion + + "]"; + if (!info.equals(r.mInfoAllowStartForeground)) { + r.mLoggedInfoAllowStartForeground = false; + r.mInfoAllowStartForeground = info; + } + return false; } } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 775119c18037..e9539be8b0d5 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -144,6 +144,13 @@ final class ActivityManagerConstants extends ContentObserver { private static final String KEY_DEFAULT_BACKGROUND_FGS_STARTS_RESTRICTION_ENABLED = "default_background_fgs_starts_restriction_enabled"; + /** + * Default value for mFlagFgsStartRestrictionEnabled if not explicitly set in + * Settings.Global. + */ + private static final String KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED = + "default_fgs_starts_restriction_enabled"; + // Maximum number of cached processes we will allow. public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES; @@ -293,6 +300,11 @@ final class ActivityManagerConstants extends ContentObserver { // started, the restriction is on while-in-use permissions.) volatile boolean mFlagBackgroundFgsStartRestrictionEnabled = true; + // Indicates whether the foreground service background start restriction is enabled. + // When the restriction is enabled, service is not allowed to startForeground from background + // at all. + volatile boolean mFlagFgsStartRestrictionEnabled = false; + private final ActivityManagerService mService; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -436,6 +448,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_DEFAULT_BACKGROUND_FGS_STARTS_RESTRICTION_ENABLED: updateBackgroundFgsStartsRestriction(); break; + case KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED: + updateFgsStartsRestriction(); + break; case KEY_OOMADJ_UPDATE_POLICY: updateOomAdjUpdatePolicy(); break; @@ -659,6 +674,7 @@ final class ActivityManagerConstants extends ContentObserver { mFlagForegroundServiceStartsLoggingEnabled = Settings.Global.getInt(mResolver, Settings.Global.FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED, 1) == 1; } + private void updateBackgroundFgsStartsRestriction() { mFlagBackgroundFgsStartRestrictionEnabled = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -666,6 +682,13 @@ final class ActivityManagerConstants extends ContentObserver { /*defaultValue*/ true); } + private void updateFgsStartsRestriction() { + mFlagFgsStartRestrictionEnabled = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED, + /*defaultValue*/ false); + } + private void updateOomAdjUpdatePolicy() { OOMADJ_UPDATE_QUICK = DeviceConfig.getInt( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index a838aadc404e..3e600b793385 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -98,7 +98,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST; @@ -146,7 +145,6 @@ import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.StackInfo; import android.app.ActivityManagerInternal; -import android.app.ActivityManagerProto; import android.app.ActivityThread; import android.app.AppGlobals; import android.app.AppOpsManager; @@ -176,7 +174,6 @@ import android.app.PendingIntent; import android.app.ProcessMemoryState; import android.app.ProfilerInfo; import android.app.WaitResult; -import android.app.backup.BackupManager; import android.app.backup.BackupManager.OperationType; import android.app.backup.IBackupManager; import android.app.usage.UsageEvents; @@ -349,7 +346,6 @@ import com.android.server.SystemServiceManager; import com.android.server.ThreadPriorityBooster; import com.android.server.UserspaceRebootLogger; import com.android.server.Watchdog; -import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto; import com.android.server.appop.AppOpsService; import com.android.server.compat.PlatformCompat; import com.android.server.contentcapture.ContentCaptureManagerInternal; @@ -490,9 +486,6 @@ public class ActivityManagerService extends IActivityManager.Stub // as one line, but close enough for now. static final int RESERVED_BYTES_PER_LOGCAT_LINE = 100; - /** If a UID observer takes more than this long, send a WTF. */ - private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20; - // Necessary ApplicationInfo flags to mark an app as persistent static final int PERSISTENT_MASK = ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT; @@ -644,9 +637,6 @@ public class ActivityManagerService extends IActivityManager.Stub @VisibleForTesting long mWaitForNetworkTimeoutMs; - /** Total # of UID change events dispatched, shown in dumpsys. */ - int mUidChangeDispatchCount; - /** * Uids of apps with current active camera sessions. Access synchronized on * the IntArray instance itself, and no other locks must be acquired while that @@ -1014,12 +1004,6 @@ public class ActivityManagerService extends IActivityManager.Stub }; /** - * This is for verifying the UID report flow. - */ - static final boolean VALIDATE_UID_STATES = true; - final ActiveUids mValidateUids = new ActiveUids(this, false /* postChangesToAtm */); - - /** * Fingerprints (hashCode()) of stack traces that we've * already logged DropBox entries for. Guarded by itself. If * something (rogue user app) forces this over @@ -1399,69 +1383,6 @@ public class ActivityManagerService extends IActivityManager.Stub int foregroundServiceTypes; } - static final class UidObserverRegistration { - final int uid; - final String pkg; - final int which; - final int cutpoint; - - /** - * Total # of callback calls that took more than {@link #SLOW_UID_OBSERVER_THRESHOLD_MS}. - * We show it in dumpsys. - */ - int mSlowDispatchCount; - - /** Max time it took for each dispatch. */ - int mMaxDispatchTime; - - final SparseIntArray lastProcStates; - - // Please keep the enum lists in sync - private static int[] ORIG_ENUMS = new int[]{ - ActivityManager.UID_OBSERVER_IDLE, - ActivityManager.UID_OBSERVER_ACTIVE, - ActivityManager.UID_OBSERVER_GONE, - ActivityManager.UID_OBSERVER_PROCSTATE, - }; - private static int[] PROTO_ENUMS = new int[]{ - ActivityManagerProto.UID_OBSERVER_FLAG_IDLE, - ActivityManagerProto.UID_OBSERVER_FLAG_ACTIVE, - ActivityManagerProto.UID_OBSERVER_FLAG_GONE, - ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE, - }; - - UidObserverRegistration(int _uid, String _pkg, int _which, int _cutpoint) { - uid = _uid; - pkg = _pkg; - which = _which; - cutpoint = _cutpoint; - if (cutpoint >= ActivityManager.MIN_PROCESS_STATE) { - lastProcStates = new SparseIntArray(); - } else { - lastProcStates = null; - } - } - - void dumpDebug(ProtoOutputStream proto, long fieldId) { - final long token = proto.start(fieldId); - proto.write(UidObserverRegistrationProto.UID, uid); - proto.write(UidObserverRegistrationProto.PACKAGE, pkg); - ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, UidObserverRegistrationProto.FLAGS, - which, ORIG_ENUMS, PROTO_ENUMS); - proto.write(UidObserverRegistrationProto.CUT_POINT, cutpoint); - if (lastProcStates != null) { - final int NI = lastProcStates.size(); - for (int i=0; i<NI; i++) { - final long pToken = proto.start(UidObserverRegistrationProto.LAST_PROC_STATES); - proto.write(UidObserverRegistrationProto.ProcState.UID, lastProcStates.keyAt(i)); - proto.write(UidObserverRegistrationProto.ProcState.STATE, lastProcStates.valueAt(i)); - proto.end(pToken); - } - } - proto.end(token); - } - } - // TODO: Move below 4 members and code to ProcessList final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>(); ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5]; @@ -1469,12 +1390,6 @@ public class ActivityManagerService extends IActivityManager.Stub final ArrayList<ProcessChangeItem> mPendingProcessChanges = new ArrayList<>(); final ArrayList<ProcessChangeItem> mAvailProcessChanges = new ArrayList<>(); - final RemoteCallbackList<IUidObserver> mUidObservers = new RemoteCallbackList<>(); - UidRecord.ChangeItem[] mActiveUidChanges = new UidRecord.ChangeItem[5]; - - final ArrayList<UidRecord.ChangeItem> mPendingUidChanges = new ArrayList<>(); - final ArrayList<UidRecord.ChangeItem> mAvailUidChanges = new ArrayList<>(); - OomAdjObserver mCurOomAdjObserver; int mCurOomAdjUid; @@ -1525,6 +1440,8 @@ public class ActivityManagerService extends IActivityManager.Stub public final ActivityManagerInternal mInternal; final ActivityThread mSystemThread; + final UidObserverController mUidObserverController; + private final class AppDeathRecipient implements IBinder.DeathRecipient { final ProcessRecord mApp; final int mPid; @@ -1570,7 +1487,6 @@ public class ActivityManagerService extends IActivityManager.Stub static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 49; static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 50; static final int ABORT_DUMPHEAP_MSG = 51; - static final int DISPATCH_UIDS_CHANGED_UI_MSG = 53; static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 56; static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 57; static final int IDLE_UIDS_MSG = 58; @@ -1581,6 +1497,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70; static final int KILL_APP_ZYGOTE_MSG = 71; static final int BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG = 72; + static final int WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG = 73; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -1698,17 +1615,14 @@ public class ActivityManagerService extends IActivityManager.Stub break; } case DISPATCH_PROCESS_DIED_UI_MSG: { + if (false) { // DO NOT SUBMIT WITH TRUE + maybeTriggerWatchdog(); + } final int pid = msg.arg1; final int uid = msg.arg2; dispatchProcessDied(pid, uid); break; } - case DISPATCH_UIDS_CHANGED_UI_MSG: { - if (false) { // DO NOT SUBMIT WITH TRUE - maybeTriggerWatchdog(); - } - dispatchUidsChanged(); - } break; case DISPATCH_OOM_ADJ_OBSERVER_MSG: { dispatchOomAdjObserver((String) msg.obj); } break; @@ -1794,12 +1708,10 @@ public class ActivityManagerService extends IActivityManager.Stub } } break; case CHECK_EXCESSIVE_POWER_USE_MSG: { - synchronized (ActivityManagerService.this) { - checkExcessivePowerUsageLocked(); - removeMessages(CHECK_EXCESSIVE_POWER_USE_MSG); - Message nmsg = obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG); - sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL); - } + checkExcessivePowerUsage(); + removeMessages(CHECK_EXCESSIVE_POWER_USE_MSG); + Message nmsg = obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG); + sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL); } break; case REPORT_MEM_USAGE_MSG: { final ArrayList<ProcessMemInfo> memInfos = (ArrayList<ProcessMemInfo>)msg.obj; @@ -1917,6 +1829,11 @@ public class ActivityManagerService extends IActivityManager.Stub case BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG: { handleBinderHeavyHitterAutoSamplerTimeOut(); } break; + case WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG: { + synchronized (ActivityManagerService.this) { + ((ContentProviderRecord) msg.obj).onProviderPublishStatusLocked(false); + } + } break; } } } @@ -2505,6 +2422,7 @@ public class ActivityManagerService extends IActivityManager.Stub mConstants = hasHandlerThread ? new ActivityManagerConstants(mContext, this, mHandler) : null; final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */); + mUidObserverController = new UidObserverController(this); mPlatformCompat = null; mProcessList = injector.getProcessList(this); mProcessList.init(this, activeUids, mPlatformCompat); @@ -2600,6 +2518,7 @@ public class ActivityManagerService extends IActivityManager.Stub mCpHelper = new ContentProviderHelper(this, true); mPackageWatchdog = PackageWatchdog.getInstance(mUiContext); mAppErrors = new AppErrors(mUiContext, this, mPackageWatchdog); + mUidObserverController = new UidObserverController(this); final File systemDir = SystemServiceManager.ensureSystemDir(); @@ -3416,153 +3335,6 @@ public class ActivityManagerService extends IActivityManager.Stub mProcessObservers.finishBroadcast(); } - @VisibleForTesting - void dispatchUidsChanged() { - int N; - synchronized (this) { - N = mPendingUidChanges.size(); - if (mActiveUidChanges.length < N) { - mActiveUidChanges = new UidRecord.ChangeItem[N]; - } - for (int i=0; i<N; i++) { - final UidRecord.ChangeItem change = mPendingUidChanges.get(i); - mActiveUidChanges[i] = change; - if (change.uidRecord != null) { - change.uidRecord.pendingChange = null; - change.uidRecord = null; - } - } - mPendingUidChanges.clear(); - if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, - "*** Delivering " + N + " uid changes"); - } - - mUidChangeDispatchCount += N; - int i = mUidObservers.beginBroadcast(); - while (i > 0) { - i--; - dispatchUidsChangedForObserver(mUidObservers.getBroadcastItem(i), - (UidObserverRegistration) mUidObservers.getBroadcastCookie(i), N); - } - mUidObservers.finishBroadcast(); - - if (VALIDATE_UID_STATES && mUidObservers.getRegisteredCallbackCount() > 0) { - for (int j = 0; j < N; ++j) { - final UidRecord.ChangeItem item = mActiveUidChanges[j]; - if ((item.change & UidRecord.CHANGE_GONE) != 0) { - mValidateUids.remove(item.uid); - } else { - UidRecord validateUid = mValidateUids.get(item.uid); - if (validateUid == null) { - validateUid = new UidRecord(item.uid); - mValidateUids.put(item.uid, validateUid); - } - if ((item.change & UidRecord.CHANGE_IDLE) != 0) { - validateUid.idle = true; - } else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) { - validateUid.idle = false; - } - validateUid.setCurProcState(validateUid.setProcState = item.processState); - validateUid.curCapability = validateUid.setCapability = item.capability; - validateUid.lastDispatchedProcStateSeq = item.procStateSeq; - } - } - } - - synchronized (this) { - for (int j = 0; j < N; j++) { - mAvailUidChanges.add(mActiveUidChanges[j]); - } - } - } - - private void dispatchUidsChangedForObserver(IUidObserver observer, - UidObserverRegistration reg, int changesSize) { - if (observer == null) { - return; - } - try { - for (int j = 0; j < changesSize; j++) { - UidRecord.ChangeItem item = mActiveUidChanges[j]; - final int change = item.change; - if (change == UidRecord.CHANGE_PROCSTATE && - (reg.which & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) { - // No-op common case: no significant change, the observer is not - // interested in all proc state changes. - continue; - } - final long start = SystemClock.uptimeMillis(); - if ((change & UidRecord.CHANGE_IDLE) != 0) { - if ((reg.which & ActivityManager.UID_OBSERVER_IDLE) != 0) { - if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, - "UID idle uid=" + item.uid); - observer.onUidIdle(item.uid, item.ephemeral); - } - } else if ((change & UidRecord.CHANGE_ACTIVE) != 0) { - if ((reg.which & ActivityManager.UID_OBSERVER_ACTIVE) != 0) { - if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, - "UID active uid=" + item.uid); - observer.onUidActive(item.uid); - } - } - if ((reg.which & ActivityManager.UID_OBSERVER_CACHED) != 0) { - if ((change & UidRecord.CHANGE_CACHED) != 0) { - if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, - "UID cached uid=" + item.uid); - observer.onUidCachedChanged(item.uid, true); - } else if ((change & UidRecord.CHANGE_UNCACHED) != 0) { - if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, - "UID active uid=" + item.uid); - observer.onUidCachedChanged(item.uid, false); - } - } - if ((change & UidRecord.CHANGE_GONE) != 0) { - if ((reg.which & ActivityManager.UID_OBSERVER_GONE) != 0) { - if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, - "UID gone uid=" + item.uid); - observer.onUidGone(item.uid, item.ephemeral); - } - if (reg.lastProcStates != null) { - reg.lastProcStates.delete(item.uid); - } - } else { - if ((reg.which & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) { - if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, - "UID CHANGED uid=" + item.uid - + ": " + item.processState + ": " + item.capability); - boolean doReport = true; - if (reg.cutpoint >= ActivityManager.MIN_PROCESS_STATE) { - final int lastState = reg.lastProcStates.get(item.uid, - ActivityManager.PROCESS_STATE_UNKNOWN); - if (lastState != ActivityManager.PROCESS_STATE_UNKNOWN) { - final boolean lastAboveCut = lastState <= reg.cutpoint; - final boolean newAboveCut = item.processState <= reg.cutpoint; - doReport = lastAboveCut != newAboveCut; - } else { - doReport = item.processState != PROCESS_STATE_NONEXISTENT; - } - } - if (doReport) { - if (reg.lastProcStates != null) { - reg.lastProcStates.put(item.uid, item.processState); - } - observer.onUidStateChanged(item.uid, item.processState, - item.procStateSeq, item.capability); - } - } - } - final int duration = (int) (SystemClock.uptimeMillis() - start); - if (reg.mMaxDispatchTime < duration) { - reg.mMaxDispatchTime = duration; - } - if (duration >= SLOW_UID_OBSERVER_THRESHOLD_MS) { - reg.mSlowDispatchCount++; - } - } - } catch (RemoteException e) { - } - } - void dispatchOomAdjObserver(String msg) { OomAdjObserver observer; synchronized (this) { @@ -4603,10 +4375,12 @@ public class ActivityManagerService extends IActivityManager.Stub proc.lastMemInfoTime = SystemClock.uptimeMillis(); if (proc.thread != null && proc.setAdj == oomAdj) { // Record this for posterity if the process has been stable. - proc.baseProcessTracker.addPss(infos[i].getTotalPss(), - infos[i].getTotalUss(), infos[i].getTotalRss(), false, - ProcessStats.ADD_PSS_EXTERNAL_SLOW, endTime - startTime, - proc.pkgList.mPkgList); + synchronized (mProcessStats.mLock) { + proc.baseProcessTracker.addPss(infos[i].getTotalPss(), + infos[i].getTotalUss(), infos[i].getTotalRss(), false, + ProcessStats.ADD_PSS_EXTERNAL_SLOW, endTime - startTime, + proc.pkgList.mPkgList); + } for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) { ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg); FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED, @@ -4663,8 +4437,11 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { if (proc.thread != null && proc.setAdj == oomAdj) { // Record this for posterity if the process has been stable. - proc.baseProcessTracker.addPss(pss[i], tmpUss[0], tmpUss[2], false, - ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime, proc.pkgList.mPkgList); + synchronized (mProcessStats.mLock) { + proc.baseProcessTracker.addPss(pss[i], tmpUss[0], tmpUss[2], false, + ProcessStats.ADD_PSS_EXTERNAL, endTime - startTime, + proc.pkgList.mPkgList); + } for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) { ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg); FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED, @@ -7494,15 +7271,15 @@ public class ActivityManagerService extends IActivityManager.Stub "registerUidObserver"); } synchronized (this) { - mUidObservers.register(observer, new UidObserverRegistration(Binder.getCallingUid(), - callingPackage, which, cutpoint)); + mUidObserverController.register(observer, which, cutpoint, callingPackage, + Binder.getCallingUid()); } } @Override public void unregisterUidObserver(IUidObserver observer) { synchronized (this) { - mUidObservers.unregister(observer); + mUidObserverController.unregister(observer); } } @@ -10121,9 +9898,9 @@ public class ActivityManagerService extends IActivityManager.Stub } if (dumpAll) { - if (mValidateUids.size() > 0) { - if (dumpUids(pw, dumpPackage, dumpAppId, mValidateUids, "UID validation:", - needSep)) { + if (mUidObserverController.mValidateUids.size() > 0) { + if (dumpUids(pw, dumpPackage, dumpAppId, mUidObserverController.mValidateUids, + "UID validation:", needSep)) { needSep = true; } } @@ -10255,45 +10032,8 @@ public class ActivityManagerService extends IActivityManager.Stub } } if (dumpAll) { - final int NI = mUidObservers.getRegisteredCallbackCount(); - boolean printed = false; - for (int i=0; i<NI; i++) { - final UidObserverRegistration reg = (UidObserverRegistration) - mUidObservers.getRegisteredCallbackCookie(i); - if (dumpPackage == null || dumpPackage.equals(reg.pkg)) { - if (!printed) { - pw.println(" mUidObservers:"); - printed = true; - } - pw.print(" "); UserHandle.formatUid(pw, reg.uid); - pw.print(" "); pw.print(reg.pkg); - final IUidObserver observer = mUidObservers.getRegisteredCallbackItem(i); - pw.print(" "); pw.print(observer.getClass().getTypeName()); pw.print(":"); - if ((reg.which&ActivityManager.UID_OBSERVER_IDLE) != 0) { - pw.print(" IDLE"); - } - if ((reg.which&ActivityManager.UID_OBSERVER_ACTIVE) != 0) { - pw.print(" ACT" ); - } - if ((reg.which&ActivityManager.UID_OBSERVER_GONE) != 0) { - pw.print(" GONE"); - } - if ((reg.which&ActivityManager.UID_OBSERVER_PROCSTATE) != 0) { - pw.print(" STATE"); - pw.print(" (cut="); pw.print(reg.cutpoint); - pw.print(")"); - } - pw.println(); - if (reg.lastProcStates != null) { - final int NJ = reg.lastProcStates.size(); - for (int j=0; j<NJ; j++) { - pw.print(" Last "); - UserHandle.formatUid(pw, reg.lastProcStates.keyAt(j)); - pw.print(": "); pw.println(reg.lastProcStates.valueAt(j)); - } - } - } - } + mUidObserverController.dump(pw, dumpPackage); + pw.println(" mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist)); pw.println(" mDeviceIdleExceptIdleWhitelist=" + Arrays.toString(mDeviceIdleExceptIdleWhitelist)); @@ -10421,25 +10161,6 @@ public class ActivityManagerService extends IActivityManager.Stub pw.print(" mLowRamSinceLastIdle="); TimeUtils.formatDuration(getLowRamTimeSinceIdle(now), pw); pw.println(); - pw.println(); - pw.print(" mUidChangeDispatchCount="); - pw.print(mUidChangeDispatchCount); - pw.println(); - - pw.println(" Slow UID dispatches:"); - final int N = mUidObservers.beginBroadcast(); - for (int i = 0; i < N; i++) { - UidObserverRegistration r = - (UidObserverRegistration) mUidObservers.getBroadcastCookie(i); - pw.print(" "); - pw.print(mUidObservers.getBroadcastItem(i).getClass().getTypeName()); - pw.print(": "); - pw.print(r.mSlowDispatchCount); - pw.print(" / Max "); - pw.print(r.mMaxDispatchTime); - pw.println("ms"); - } - mUidObservers.finishBroadcast(); pw.println(); pw.println(" ServiceManager statistics:"); @@ -10507,8 +10228,8 @@ public class ActivityManagerService extends IActivityManager.Stub uidRec.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS); } - for (int i = 0; i < mValidateUids.size(); i++) { - UidRecord uidRec = mValidateUids.valueAt(i); + for (int i = 0; i < mUidObserverController.mValidateUids.size(); i++) { + UidRecord uidRec = mUidObserverController.mValidateUids.valueAt(i); if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) { continue; } @@ -10593,14 +10314,7 @@ public class ActivityManagerService extends IActivityManager.Stub ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER); } - final int NI = mUidObservers.getRegisteredCallbackCount(); - for (int i=0; i<NI; i++) { - final UidObserverRegistration reg = (UidObserverRegistration) - mUidObservers.getRegisteredCallbackCookie(i); - if (dumpPackage == null || dumpPackage.equals(reg.pkg)) { - reg.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS); - } - } + mUidObserverController.dumpDebug(proto, dumpPackage); for (int v : mDeviceIdleWhitelist) { proto.write(ActivityManagerServiceDumpProcessesProto.DEVICE_IDLE_WHITELIST, v); @@ -12101,8 +11815,10 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { if (r.thread != null && oomAdj == r.getSetAdjWithServices()) { // Record this for posterity if the process has been stable. - r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true, - reportType, endTime-startTime, r.pkgList.mPkgList); + synchronized (mProcessStats.mLock) { + r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true, + reportType, endTime - startTime, r.pkgList.mPkgList); + } for (int ipkg = r.pkgList.size() - 1; ipkg >= 0; ipkg--) { ProcessStats.ProcessStateHolder holder = r.pkgList.valueAt(ipkg); FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED, @@ -12707,8 +12423,10 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { if (r.thread != null && oomAdj == r.getSetAdjWithServices()) { // Record this for posterity if the process has been stable. - r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true, - reportType, endTime-startTime, r.pkgList.mPkgList); + synchronized (mProcessStats.mLock) { + r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true, + reportType, endTime - startTime, r.pkgList.mPkgList); + } for (int ipkg = r.pkgList.size() - 1; ipkg >= 0; ipkg--) { ProcessStats.ProcessStateHolder holder = r.pkgList.valueAt(ipkg); FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED, @@ -15921,8 +15639,10 @@ public class ActivityManagerService extends IActivityManager.Stub EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024, swapPss * 1024, rss * 1024, statType, procState, pssDuration); proc.lastPssTime = now; - proc.baseProcessTracker.addPss( - pss, uss, rss, true, statType, pssDuration, proc.pkgList.mPkgList); + synchronized (mProcessStats.mLock) { + proc.baseProcessTracker.addPss( + pss, uss, rss, true, statType, pssDuration, proc.pkgList.mPkgList); + } for (int ipkg = proc.pkgList.mPkgList.size() - 1; ipkg >= 0; ipkg--) { ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg); FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED, @@ -16224,182 +15944,94 @@ public class ActivityManagerService extends IActivityManager.Stub } } - final void checkExcessivePowerUsageLocked() { + private void checkExcessivePowerUsage() { updateCpuStatsNow(); - BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); - boolean doCpuKills = true; - if (mLastPowerCheckUptime == 0) { - doCpuKills = false; - } - final long curUptime = SystemClock.uptimeMillis(); - final long uptimeSince = curUptime - mLastPowerCheckUptime; - mLastPowerCheckUptime = curUptime; - int i = mProcessList.mLruProcesses.size(); - while (i > 0) { - i--; - ProcessRecord app = mProcessList.mLruProcesses.get(i); - if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) { - if (app.lastCpuTime <= 0) { - continue; - } - long cputimeUsed = app.curCpuTime - app.lastCpuTime; - if (DEBUG_POWER) { - StringBuilder sb = new StringBuilder(128); - sb.append("CPU for "); - app.toShortString(sb); - sb.append(": over "); - TimeUtils.formatDuration(uptimeSince, sb); - sb.append(" used "); - TimeUtils.formatDuration(cputimeUsed, sb); - sb.append(" ("); - sb.append((cputimeUsed*100)/uptimeSince); - sb.append("%)"); - Slog.i(TAG_POWER, sb.toString()); - } - // If the process has used too much CPU over the last duration, the - // user probably doesn't want this, so kill! - if (doCpuKills && uptimeSince > 0) { - // What is the limit for this process? - int cpuLimit; - long checkDur = curUptime - app.getWhenUnimportant(); - if (checkDur <= mConstants.POWER_CHECK_INTERVAL) { - cpuLimit = mConstants.POWER_CHECK_MAX_CPU_1; - } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL*2) - || app.setProcState <= ActivityManager.PROCESS_STATE_HOME) { - cpuLimit = mConstants.POWER_CHECK_MAX_CPU_2; - } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL*3)) { - cpuLimit = mConstants.POWER_CHECK_MAX_CPU_3; - } else { - cpuLimit = mConstants.POWER_CHECK_MAX_CPU_4; + synchronized (this) { + BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + boolean doCpuKills = true; + if (mLastPowerCheckUptime == 0) { + doCpuKills = false; + } + final long curUptime = SystemClock.uptimeMillis(); + final long uptimeSince = curUptime - mLastPowerCheckUptime; + mLastPowerCheckUptime = curUptime; + int i = mProcessList.mLruProcesses.size(); + while (i > 0) { + i--; + ProcessRecord app = mProcessList.mLruProcesses.get(i); + if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) { + if (app.lastCpuTime <= 0) { + continue; + } + long cputimeUsed = app.curCpuTime - app.lastCpuTime; + if (DEBUG_POWER) { + StringBuilder sb = new StringBuilder(128); + sb.append("CPU for "); + app.toShortString(sb); + sb.append(": over "); + TimeUtils.formatDuration(uptimeSince, sb); + sb.append(" used "); + TimeUtils.formatDuration(cputimeUsed, sb); + sb.append(" ("); + sb.append((cputimeUsed * 100) / uptimeSince); + sb.append("%)"); + Slog.i(TAG_POWER, sb.toString()); } - if (((cputimeUsed*100)/uptimeSince) >= cpuLimit) { - synchronized (stats) { - stats.reportExcessiveCpuLocked(app.info.uid, app.processName, - uptimeSince, cputimeUsed); + // If the process has used too much CPU over the last duration, the + // user probably doesn't want this, so kill! + if (doCpuKills && uptimeSince > 0) { + // What is the limit for this process? + int cpuLimit; + long checkDur = curUptime - app.getWhenUnimportant(); + if (checkDur <= mConstants.POWER_CHECK_INTERVAL) { + cpuLimit = mConstants.POWER_CHECK_MAX_CPU_1; + } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 2) + || app.setProcState <= ActivityManager.PROCESS_STATE_HOME) { + cpuLimit = mConstants.POWER_CHECK_MAX_CPU_2; + } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 3)) { + cpuLimit = mConstants.POWER_CHECK_MAX_CPU_3; + } else { + cpuLimit = mConstants.POWER_CHECK_MAX_CPU_4; } - app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince - + " dur=" + checkDur + " limit=" + cpuLimit, - ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE, - ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU, - true); - app.baseProcessTracker.reportExcessiveCpu(app.pkgList.mPkgList); - for (int ipkg = app.pkgList.size() - 1; ipkg >= 0; ipkg--) { - ProcessStats.ProcessStateHolder holder = app.pkgList.valueAt(ipkg); - FrameworkStatsLog.write(FrameworkStatsLog.EXCESSIVE_CPU_USAGE_REPORTED, - app.info.uid, - holder.state.getName(), - holder.state.getPackage(), - holder.appVersion); + if (((cputimeUsed * 100) / uptimeSince) >= cpuLimit) { + synchronized (stats) { + stats.reportExcessiveCpuLocked(app.info.uid, app.processName, + uptimeSince, cputimeUsed); + } + app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince + + " dur=" + checkDur + " limit=" + cpuLimit, + ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE, + ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU, + true); + synchronized (mProcessStats.mLock) { + app.baseProcessTracker.reportExcessiveCpu(app.pkgList.mPkgList); + } + for (int ipkg = app.pkgList.size() - 1; ipkg >= 0; ipkg--) { + ProcessStats.ProcessStateHolder holder = app.pkgList.valueAt(ipkg); + FrameworkStatsLog.write( + FrameworkStatsLog.EXCESSIVE_CPU_USAGE_REPORTED, + app.info.uid, + holder.state.getName(), + holder.state.getPackage(), + holder.appVersion); + } } } + app.lastCpuTime = app.curCpuTime; } - app.lastCpuTime = app.curCpuTime; - } - } - } - - private boolean isEphemeralLocked(int uid) { - String packages[] = mContext.getPackageManager().getPackagesForUid(uid); - if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid - return false; - } - return getPackageManagerInternalLocked().isPackageEphemeral(UserHandle.getUserId(uid), - packages[0]); - } - - @VisibleForTesting - final void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) { - final UidRecord.ChangeItem pendingChange; - if (uidRec == null || uidRec.pendingChange == null) { - if (mPendingUidChanges.size() == 0) { - if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, - "*** Enqueueing dispatch uid changed!"); - mUiHandler.obtainMessage(DISPATCH_UIDS_CHANGED_UI_MSG).sendToTarget(); - } - final int NA = mAvailUidChanges.size(); - if (NA > 0) { - pendingChange = mAvailUidChanges.remove(NA-1); - if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, - "Retrieving available item: " + pendingChange); - } else { - pendingChange = new UidRecord.ChangeItem(); - if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, - "Allocating new item: " + pendingChange); - } - if (uidRec != null) { - uidRec.pendingChange = pendingChange; - if ((change & UidRecord.CHANGE_GONE) != 0 && !uidRec.idle) { - // If this uid is going away, and we haven't yet reported it is gone, - // then do so now. - change |= UidRecord.CHANGE_IDLE; - } - } else if (uid < 0) { - throw new IllegalArgumentException("No UidRecord or uid"); - } - pendingChange.uidRecord = uidRec; - pendingChange.uid = uidRec != null ? uidRec.uid : uid; - mPendingUidChanges.add(pendingChange); - } else { - pendingChange = uidRec.pendingChange; - // If there is no change in idle or active state, then keep whatever was pending. - if ((change & (UidRecord.CHANGE_IDLE | UidRecord.CHANGE_ACTIVE)) == 0) { - change |= (pendingChange.change & (UidRecord.CHANGE_IDLE - | UidRecord.CHANGE_ACTIVE)); - } - // If there is no change in cached or uncached state, then keep whatever was pending. - if ((change & (UidRecord.CHANGE_CACHED | UidRecord.CHANGE_UNCACHED)) == 0) { - change |= (pendingChange.change & (UidRecord.CHANGE_CACHED - | UidRecord.CHANGE_UNCACHED)); - } - // If this is a report of the UID being gone, then we shouldn't keep any previous - // report of it being active or cached. (That is, a gone uid is never active, - // and never cached.) - if ((change & UidRecord.CHANGE_GONE) != 0) { - change &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED); - if (!uidRec.idle) { - // If this uid is going away, and we haven't yet reported it is gone, - // then do so now. - change |= UidRecord.CHANGE_IDLE; - } - } - } - pendingChange.change = change; - pendingChange.processState = uidRec != null ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT; - pendingChange.capability = uidRec != null ? uidRec.setCapability : 0; - pendingChange.ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid); - pendingChange.procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0; - if (uidRec != null) { - uidRec.lastReportedChange = change; - uidRec.updateLastDispatchedProcStateSeq(change); - } - - // Directly update the power manager, since we sit on top of it and it is critical - // it be kept in sync (so wake locks will be held as soon as appropriate). - if (mLocalPowerManager != null) { - // TO DO: dispatch cached/uncached changes here, so we don't need to report - // all proc state changes. - if ((change & UidRecord.CHANGE_ACTIVE) != 0) { - mLocalPowerManager.uidActive(pendingChange.uid); - } - if ((change & UidRecord.CHANGE_IDLE) != 0) { - mLocalPowerManager.uidIdle(pendingChange.uid); - } - if ((change & UidRecord.CHANGE_GONE) != 0) { - mLocalPowerManager.uidGone(pendingChange.uid); - } else { - mLocalPowerManager.updateUidProcState(pendingChange.uid, - pendingChange.processState); } } } final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) { - if (proc.thread != null && proc.baseProcessTracker != null) { - final int procState = proc.getReportedProcState(); - if (procState != PROCESS_STATE_NONEXISTENT) { - proc.baseProcessTracker.setState( - procState, memFactor, now, proc.pkgList.mPkgList); + synchronized (mProcessStats.mLock) { + if (proc.thread != null && proc.baseProcessTracker != null) { + final int procState = proc.getReportedProcState(); + if (procState != PROCESS_STATE_NONEXISTENT) { + proc.baseProcessTracker.setState( + procState, memFactor, now, proc.pkgList.mPkgList); + } } } } @@ -16812,7 +16444,7 @@ public class ActivityManagerService extends IActivityManager.Stub @GuardedBy("this") final void doStopUidLocked(int uid, final UidRecord uidRec) { mServices.stopInBackgroundLocked(uid); - enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE); + mUidObserverController.enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE); } /** @@ -18473,7 +18105,8 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.w(TAG_NETWORK, "Total time waited for network rules to get updated: " + totalTime + ". Uid: " + callingUid + " procStateSeq: " + procStateSeq + " UidRec: " + record - + " validateUidRec: " + mValidateUids.get(callingUid)); + + " validateUidRec: " + + mUidObserverController.mValidateUids.get(callingUid)); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index 5cc7aba736e1..bfba4afcd4e4 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -50,6 +50,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.IBinder; +import android.os.Message; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; @@ -218,7 +219,7 @@ public class ContentProviderHelper { // of being published... but it is also allowed to run // in the caller's process, so don't make a connection // and just let the caller instantiate its own instance. - ContentProviderHolder holder = cpr.newHolder(null); + ContentProviderHolder holder = cpr.newHolder(null, true); // don't give caller the provider object, it needs to make its own. holder.provider = null; return holder; @@ -415,7 +416,7 @@ public class ContentProviderHelper { // info and allow the caller to instantiate it. Only do // this if the provider is the same user as the caller's // process, or can run as root (so can be in any process). - return cpr.newHolder(null); + return cpr.newHolder(null, true); } if (ActivityManagerDebugConfig.DEBUG_PROVIDER) { @@ -513,6 +514,38 @@ public class ContentProviderHelper { UserHandle.getAppId(cpi.applicationInfo.uid)); } + if (caller != null) { + // The client will be waiting, and we'll notify it when the provider is ready. + synchronized (cpr) { + if (cpr.provider == null) { + if (cpr.launchingApp == null) { + Slog.w(TAG, "Unable to launch app " + + cpi.applicationInfo.packageName + "/" + + cpi.applicationInfo.uid + " for provider " + + name + ": launching app became null"); + EventLogTags.writeAmProviderLostProcess( + UserHandle.getUserId(cpi.applicationInfo.uid), + cpi.applicationInfo.packageName, + cpi.applicationInfo.uid, name); + return null; + } + + if (conn != null) { + conn.waiting = true; + } + Message msg = mService.mHandler.obtainMessage( + ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG); + msg.obj = cpr; + mService.mHandler.sendMessageDelayed(msg, + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS); + } + } + // Return a holder instance even if we are waiting for the publishing of the provider, + // client will check for the holder.provider to see if it needs to wait for it. + return cpr.newHolder(conn, false); + } + + // Because of the provider's external client (i.e., SHELL), we'll have to wait right here. // Wait for the provider to be published... final long timeout = SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS; @@ -569,7 +602,7 @@ public class ContentProviderHelper { + " caller=" + callerName + "/" + Binder.getCallingUid()); return null; } - return cpr.newHolder(conn); + return cpr.newHolder(conn, false); } void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) { @@ -622,6 +655,8 @@ public class ContentProviderHelper { } if (wasInLaunchingProviders) { mService.mHandler.removeMessages( + ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG, dst); + mService.mHandler.removeMessages( ActivityManagerService.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r); } // Make sure the package is associated with the process. @@ -635,6 +670,7 @@ public class ContentProviderHelper { dst.provider = src.provider; dst.setProcess(r); dst.notifyAll(); + dst.onProviderPublishStatusLocked(true); } dst.mRestartCount = 0; mService.updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER); @@ -1504,6 +1540,9 @@ public class ContentProviderHelper { synchronized (cpr) { cpr.launchingApp = null; cpr.notifyAll(); + cpr.onProviderPublishStatusLocked(false); + mService.mHandler.removeMessages( + ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG, cpr); } final int userId = UserHandle.getUserId(cpr.uid); // Don't remove from provider map if it doesn't match diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java index d8d8cccc05f3..fb8b5d480b27 100644 --- a/services/core/java/com/android/server/am/ContentProviderRecord.java +++ b/services/core/java/com/android/server/am/ContentProviderRecord.java @@ -85,11 +85,12 @@ final class ContentProviderRecord implements ComponentName.WithComponentName { noReleaseNeeded = cpr.noReleaseNeeded; } - public ContentProviderHolder newHolder(ContentProviderConnection conn) { + public ContentProviderHolder newHolder(ContentProviderConnection conn, boolean local) { ContentProviderHolder holder = new ContentProviderHolder(info); holder.provider = provider; holder.noReleaseNeeded = noReleaseNeeded; holder.connection = conn; + holder.mLocal = local; return holder; } @@ -179,6 +180,50 @@ final class ContentProviderRecord implements ComponentName.WithComponentName { return !connections.isEmpty() || hasExternalProcessHandles(); } + /** + * Notify all clients that the provider has been published and ready to use, + * or timed out. + * + * @param status true: successfully published; false: timed out + */ + void onProviderPublishStatusLocked(boolean status) { + final int numOfConns = connections.size(); + final int userId = UserHandle.getUserId(appInfo.uid); + for (int i = 0; i < numOfConns; i++) { + final ContentProviderConnection conn = connections.get(i); + if (conn.waiting && conn.client != null) { + final ProcessRecord client = conn.client; + if (!status) { + if (launchingApp == null) { + Slog.w(TAG_AM, "Unable to launch app " + + appInfo.packageName + "/" + + appInfo.uid + " for provider " + + info.authority + ": launching app became null"); + EventLogTags.writeAmProviderLostProcess( + userId, + appInfo.packageName, + appInfo.uid, info.authority); + } else { + Slog.wtf(TAG_AM, "Timeout waiting for provider " + + appInfo.packageName + "/" + + appInfo.uid + " for provider " + + info.authority + + " caller=" + client); + } + } + if (client.thread != null) { + try { + client.thread.notifyContentProviderPublishStatus( + newHolder(status ? conn : null, false), + info.authority, userId, status); + } catch (RemoteException e) { + } + } + } + conn.waiting = false; + } + } + void dump(PrintWriter pw, String prefix, boolean full) { if (full) { pw.print(prefix); pw.print("package="); diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 0f2dfcc699e2..757b4e83e120 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -100,15 +100,20 @@ final class CoreSettingsObserver extends ContentObserver { sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS_GLES, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYER_APP, String.class); - sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_ALL_APPS, int.class); - sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_OPT_IN_APPS, String.class); + sGlobalSettingToTypeMap.put(Settings.Global.UPDATABLE_DRIVER_ALL_APPS, int.class); sGlobalSettingToTypeMap.put( - Settings.Global.GAME_DRIVER_PRERELEASE_OPT_IN_APPS, String.class); - sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_OPT_OUT_APPS, String.class); - sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_DENYLIST, String.class); - sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_ALLOWLIST, String.class); - sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_DENYLISTS, String.class); - sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, String.class); + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS, String.class); + sGlobalSettingToTypeMap.put( + Settings.Global.UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS, String.class); + sGlobalSettingToTypeMap.put( + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS, String.class); + sGlobalSettingToTypeMap.put( + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST, String.class); + sGlobalSettingToTypeMap.put( + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST, String.class); + sGlobalSettingToTypeMap.put( + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLISTS, String.class); + sGlobalSettingToTypeMap.put(Settings.Global.UPDATABLE_DRIVER_SPHAL_LIBRARIES, String.class); // add other global settings here... sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>( diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index bf15f1737cfc..bad042cd3a68 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -987,7 +987,7 @@ public final class OomAdjuster { uidRec.setWhitelist = uidRec.curWhitelist; uidRec.setIdle = uidRec.idle; mService.mAtmInternal.onUidProcStateChanged(uidRec.uid, uidRec.setProcState); - mService.enqueueUidChangeLocked(uidRec, -1, uidChange); + mService.mUidObserverController.enqueueUidChangeLocked(uidRec, -1, uidChange); mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState(), uidRec.curCapability); if (uidRec.foregroundServices) { diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 5721fb7f5128..4673ecde7a68 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2222,7 +2222,8 @@ public final class ProcessList { // access /mnt/user anyway. && app.mountMode != Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE && app.mountMode != Zygote.MOUNT_EXTERNAL_PASS_THROUGH - && app.mountMode != Zygote.MOUNT_EXTERNAL_INSTALLER; + && app.mountMode != Zygote.MOUNT_EXTERNAL_INSTALLER + && app.mountMode != Zygote.MOUNT_EXTERNAL_NONE; } private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint, @@ -2960,7 +2961,8 @@ public final class ProcessList { // No more processes using this uid, tell clients it is gone. if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, "No more processes in " + uidRecord); - mService.enqueueUidChangeLocked(uidRecord, -1, UidRecord.CHANGE_GONE); + mService.mUidObserverController.enqueueUidChangeLocked(uidRecord, -1, + UidRecord.CHANGE_GONE); EventLogTags.writeAmUidStopped(uid); mActiveUids.remove(uid); mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT, diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index e3e13391a8b0..14bc6cbed7d7 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -1079,8 +1079,8 @@ class ProcessRecord implements WindowProcessListener { */ public void resetPackageList(ProcessStatsService tracker) { final int N = pkgList.size(); - if (baseProcessTracker != null) { - synchronized (tracker.mLock) { + synchronized (tracker.mLock) { + if (baseProcessTracker != null) { long now = SystemClock.uptimeMillis(); baseProcessTracker.setState(ProcessStats.STATE_NOTHING, tracker.getMemFactorLocked(), now, pkgList.mPkgList); @@ -1108,10 +1108,11 @@ class ProcessRecord implements WindowProcessListener { holder.state.makeActive(); } } + } else if (N != 1) { + pkgList.clear(); + pkgList.put(info.packageName, + new ProcessStats.ProcessStateHolder(info.longVersionCode)); } - } else if (N != 1) { - pkgList.clear(); - pkgList.put(info.packageName, new ProcessStats.ProcessStateHolder(info.longVersionCode)); } } diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 4a2703056871..66677b67b6aa 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -146,6 +146,12 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // the most recent package that start/bind this service. String mRecentCallingPackage; + // allow the service becomes foreground service? Service started from background may not be + // allowed to become a foreground service. + boolean mAllowStartForeground; + String mInfoAllowStartForeground; + boolean mLoggedInfoAllowStartForeground; + String stringName; // caching of toString private int lastStartId; // identifier of most recent start request. @@ -408,6 +414,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN pw.println(mAllowWhileInUsePermissionInFgs); pw.print(prefix); pw.print("recentCallingPackage="); pw.println(mRecentCallingPackage); + pw.print(prefix); pw.print("allowStartForeground="); + pw.println(mAllowStartForeground); + pw.print(prefix); pw.print("infoAllowStartForeground="); + pw.println(mInfoAllowStartForeground); if (delayed) { pw.print(prefix); pw.print("delayed="); pw.println(delayed); } diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java new file mode 100644 index 000000000000..4d9260aec62e --- /dev/null +++ b/services/core/java/com/android/server/am/UidObserverController.java @@ -0,0 +1,470 @@ +/* + * Copyright (C) 2020 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 com.android.server.am; + +import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; + +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS; +import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS; + +import android.app.ActivityManager; +import android.app.ActivityManagerProto; +import android.app.IUidObserver; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.UserHandle; +import android.util.Slog; +import android.util.SparseIntArray; +import android.util.proto.ProtoOutputStream; +import android.util.proto.ProtoUtils; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto; + +import java.io.PrintWriter; +import java.util.ArrayList; + +public class UidObserverController { + private final ActivityManagerService mService; + final RemoteCallbackList<IUidObserver> mUidObservers = new RemoteCallbackList<>(); + + UidRecord.ChangeItem[] mActiveUidChanges = new UidRecord.ChangeItem[5]; + final ArrayList<UidRecord.ChangeItem> mPendingUidChanges = new ArrayList<>(); + final ArrayList<UidRecord.ChangeItem> mAvailUidChanges = new ArrayList<>(); + + /** Total # of UID change events dispatched, shown in dumpsys. */ + int mUidChangeDispatchCount; + + /** If a UID observer takes more than this long, send a WTF. */ + private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20; + + /** + * This is for verifying the UID report flow. + */ + static final boolean VALIDATE_UID_STATES = true; + final ActiveUids mValidateUids; + + UidObserverController(ActivityManagerService service) { + mService = service; + mValidateUids = new ActiveUids(mService, false /* postChangesToAtm */); + } + + @GuardedBy("mService") + void register(IUidObserver observer, int which, int cutpoint, String callingPackage, + int callingUid) { + mUidObservers.register(observer, new UidObserverRegistration(callingUid, + callingPackage, which, cutpoint)); + } + + @GuardedBy("mService") + void unregister(IUidObserver observer) { + mUidObservers.unregister(observer); + } + + @GuardedBy("mService") + final void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) { + final UidRecord.ChangeItem pendingChange; + if (uidRec == null || uidRec.pendingChange == null) { + if (mPendingUidChanges.size() == 0) { + if (DEBUG_UID_OBSERVERS) { + Slog.i(TAG_UID_OBSERVERS, "*** Enqueueing dispatch uid changed!"); + } + mService.mUiHandler.post(this::dispatchUidsChanged); + } + final int size = mAvailUidChanges.size(); + if (size > 0) { + pendingChange = mAvailUidChanges.remove(size - 1); + if (DEBUG_UID_OBSERVERS) { + Slog.i(TAG_UID_OBSERVERS, "Retrieving available item: " + pendingChange); + } + } else { + pendingChange = new UidRecord.ChangeItem(); + if (DEBUG_UID_OBSERVERS) { + Slog.i(TAG_UID_OBSERVERS, "Allocating new item: " + pendingChange); + } + } + if (uidRec != null) { + uidRec.pendingChange = pendingChange; + if ((change & UidRecord.CHANGE_GONE) != 0 && !uidRec.idle) { + // If this uid is going away, and we haven't yet reported it is gone, + // then do so now. + change |= UidRecord.CHANGE_IDLE; + } + } else if (uid < 0) { + throw new IllegalArgumentException("No UidRecord or uid"); + } + pendingChange.uidRecord = uidRec; + pendingChange.uid = uidRec != null ? uidRec.uid : uid; + mPendingUidChanges.add(pendingChange); + } else { + pendingChange = uidRec.pendingChange; + // If there is no change in idle or active state, then keep whatever was pending. + if ((change & (UidRecord.CHANGE_IDLE | UidRecord.CHANGE_ACTIVE)) == 0) { + change |= (pendingChange.change & (UidRecord.CHANGE_IDLE + | UidRecord.CHANGE_ACTIVE)); + } + // If there is no change in cached or uncached state, then keep whatever was pending. + if ((change & (UidRecord.CHANGE_CACHED | UidRecord.CHANGE_UNCACHED)) == 0) { + change |= (pendingChange.change & (UidRecord.CHANGE_CACHED + | UidRecord.CHANGE_UNCACHED)); + } + // If this is a report of the UID being gone, then we shouldn't keep any previous + // report of it being active or cached. (That is, a gone uid is never active, + // and never cached.) + if ((change & UidRecord.CHANGE_GONE) != 0) { + change &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED); + if (!uidRec.idle) { + // If this uid is going away, and we haven't yet reported it is gone, + // then do so now. + change |= UidRecord.CHANGE_IDLE; + } + } + } + pendingChange.change = change; + pendingChange.processState = uidRec != null + ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT; + pendingChange.capability = uidRec != null ? uidRec.setCapability : 0; + pendingChange.ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid); + pendingChange.procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0; + if (uidRec != null) { + uidRec.lastReportedChange = change; + uidRec.updateLastDispatchedProcStateSeq(change); + } + + // Directly update the power manager, since we sit on top of it and it is critical + // it be kept in sync (so wake locks will be held as soon as appropriate). + if (mService.mLocalPowerManager != null) { + // TO DO: dispatch cached/uncached changes here, so we don't need to report + // all proc state changes. + if ((change & UidRecord.CHANGE_ACTIVE) != 0) { + mService.mLocalPowerManager.uidActive(pendingChange.uid); + } + if ((change & UidRecord.CHANGE_IDLE) != 0) { + mService.mLocalPowerManager.uidIdle(pendingChange.uid); + } + if ((change & UidRecord.CHANGE_GONE) != 0) { + mService.mLocalPowerManager.uidGone(pendingChange.uid); + } else { + mService.mLocalPowerManager.updateUidProcState(pendingChange.uid, + pendingChange.processState); + } + } + } + + @VisibleForTesting + void dispatchUidsChanged() { + int numUidChanges; + synchronized (mService) { + numUidChanges = mPendingUidChanges.size(); + if (mActiveUidChanges.length < numUidChanges) { + mActiveUidChanges = new UidRecord.ChangeItem[numUidChanges]; + } + for (int i = 0; i < numUidChanges; i++) { + final UidRecord.ChangeItem change = mPendingUidChanges.get(i); + mActiveUidChanges[i] = change; + if (change.uidRecord != null) { + change.uidRecord.pendingChange = null; + change.uidRecord = null; + } + } + mPendingUidChanges.clear(); + if (DEBUG_UID_OBSERVERS) { + Slog.i(TAG_UID_OBSERVERS, "*** Delivering " + numUidChanges + " uid changes"); + } + } + + mUidChangeDispatchCount += numUidChanges; + int i = mUidObservers.beginBroadcast(); + while (i > 0) { + i--; + dispatchUidsChangedForObserver(mUidObservers.getBroadcastItem(i), + (UidObserverRegistration) mUidObservers.getBroadcastCookie(i), numUidChanges); + } + mUidObservers.finishBroadcast(); + + if (VALIDATE_UID_STATES && mUidObservers.getRegisteredCallbackCount() > 0) { + for (int j = 0; j < numUidChanges; ++j) { + final UidRecord.ChangeItem item = mActiveUidChanges[j]; + if ((item.change & UidRecord.CHANGE_GONE) != 0) { + mValidateUids.remove(item.uid); + } else { + UidRecord validateUid = mValidateUids.get(item.uid); + if (validateUid == null) { + validateUid = new UidRecord(item.uid); + mValidateUids.put(item.uid, validateUid); + } + if ((item.change & UidRecord.CHANGE_IDLE) != 0) { + validateUid.idle = true; + } else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) { + validateUid.idle = false; + } + validateUid.setCurProcState(validateUid.setProcState = item.processState); + validateUid.curCapability = validateUid.setCapability = item.capability; + validateUid.lastDispatchedProcStateSeq = item.procStateSeq; + } + } + } + + synchronized (mService) { + for (int j = 0; j < numUidChanges; j++) { + mAvailUidChanges.add(mActiveUidChanges[j]); + } + } + } + + private void dispatchUidsChangedForObserver(IUidObserver observer, + UidObserverRegistration reg, int changesSize) { + if (observer == null) { + return; + } + try { + for (int j = 0; j < changesSize; j++) { + UidRecord.ChangeItem item = mActiveUidChanges[j]; + final int change = item.change; + if (change == UidRecord.CHANGE_PROCSTATE + && (reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) { + // No-op common case: no significant change, the observer is not + // interested in all proc state changes. + continue; + } + final long start = SystemClock.uptimeMillis(); + if ((change & UidRecord.CHANGE_IDLE) != 0) { + if ((reg.mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) { + if (DEBUG_UID_OBSERVERS) { + Slog.i(TAG_UID_OBSERVERS, "UID idle uid=" + item.uid); + } + observer.onUidIdle(item.uid, item.ephemeral); + } + } else if ((change & UidRecord.CHANGE_ACTIVE) != 0) { + if ((reg.mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) { + if (DEBUG_UID_OBSERVERS) { + Slog.i(TAG_UID_OBSERVERS, "UID active uid=" + item.uid); + } + observer.onUidActive(item.uid); + } + } + if ((reg.mWhich & ActivityManager.UID_OBSERVER_CACHED) != 0) { + if ((change & UidRecord.CHANGE_CACHED) != 0) { + if (DEBUG_UID_OBSERVERS) { + Slog.i(TAG_UID_OBSERVERS, "UID cached uid=" + item.uid); + } + observer.onUidCachedChanged(item.uid, true); + } else if ((change & UidRecord.CHANGE_UNCACHED) != 0) { + if (DEBUG_UID_OBSERVERS) { + Slog.i(TAG_UID_OBSERVERS, "UID active uid=" + item.uid); + } + observer.onUidCachedChanged(item.uid, false); + } + } + if ((change & UidRecord.CHANGE_GONE) != 0) { + if ((reg.mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) { + if (DEBUG_UID_OBSERVERS) { + Slog.i(TAG_UID_OBSERVERS, "UID gone uid=" + item.uid); + } + observer.onUidGone(item.uid, item.ephemeral); + } + if (reg.mLastProcStates != null) { + reg.mLastProcStates.delete(item.uid); + } + } else { + if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) { + if (DEBUG_UID_OBSERVERS) { + Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid + + ": " + item.processState + ": " + item.capability); + } + boolean doReport = true; + if (reg.mCutpoint >= ActivityManager.MIN_PROCESS_STATE) { + final int lastState = reg.mLastProcStates.get(item.uid, + ActivityManager.PROCESS_STATE_UNKNOWN); + if (lastState != ActivityManager.PROCESS_STATE_UNKNOWN) { + final boolean lastAboveCut = lastState <= reg.mCutpoint; + final boolean newAboveCut = item.processState <= reg.mCutpoint; + doReport = lastAboveCut != newAboveCut; + } else { + doReport = item.processState != PROCESS_STATE_NONEXISTENT; + } + } + if (doReport) { + if (reg.mLastProcStates != null) { + reg.mLastProcStates.put(item.uid, item.processState); + } + observer.onUidStateChanged(item.uid, item.processState, + item.procStateSeq, item.capability); + } + } + } + final int duration = (int) (SystemClock.uptimeMillis() - start); + if (reg.mMaxDispatchTime < duration) { + reg.mMaxDispatchTime = duration; + } + if (duration >= SLOW_UID_OBSERVER_THRESHOLD_MS) { + reg.mSlowDispatchCount++; + } + } + } catch (RemoteException e) { + } + } + + private boolean isEphemeralLocked(int uid) { + final String[] packages = mService.mContext.getPackageManager().getPackagesForUid(uid); + if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid + return false; + } + return mService.getPackageManagerInternalLocked().isPackageEphemeral( + UserHandle.getUserId(uid), packages[0]); + } + + @GuardedBy("mService") + void dump(PrintWriter pw, String dumpPackage) { + final int count = mUidObservers.getRegisteredCallbackCount(); + boolean printed = false; + for (int i = 0; i < count; i++) { + final UidObserverRegistration reg = (UidObserverRegistration) + mUidObservers.getRegisteredCallbackCookie(i); + if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) { + if (!printed) { + pw.println(" mUidObservers:"); + printed = true; + } + pw.print(" "); UserHandle.formatUid(pw, reg.mUid); + pw.print(" "); pw.print(reg.mPkg); + final IUidObserver observer = mUidObservers.getRegisteredCallbackItem(i); + pw.print(" "); pw.print(observer.getClass().getTypeName()); pw.print(":"); + if ((reg.mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) { + pw.print(" IDLE"); + } + if ((reg.mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) { + pw.print(" ACT"); + } + if ((reg.mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) { + pw.print(" GONE"); + } + if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) { + pw.print(" STATE"); + pw.print(" (cut="); pw.print(reg.mCutpoint); + pw.print(")"); + } + pw.println(); + if (reg.mLastProcStates != null) { + final int size = reg.mLastProcStates.size(); + for (int j = 0; j < size; j++) { + pw.print(" Last "); + UserHandle.formatUid(pw, reg.mLastProcStates.keyAt(j)); + pw.print(": "); pw.println(reg.mLastProcStates.valueAt(j)); + } + } + } + } + + pw.println(); + pw.print(" mUidChangeDispatchCount="); + pw.print(mUidChangeDispatchCount); + pw.println(); + pw.println(" Slow UID dispatches:"); + final int size = mUidObservers.beginBroadcast(); + for (int i = 0; i < size; i++) { + UidObserverRegistration r = + (UidObserverRegistration) mUidObservers.getBroadcastCookie(i); + pw.print(" "); + pw.print(mUidObservers.getBroadcastItem(i).getClass().getTypeName()); + pw.print(": "); + pw.print(r.mSlowDispatchCount); + pw.print(" / Max "); + pw.print(r.mMaxDispatchTime); + pw.println("ms"); + } + mUidObservers.finishBroadcast(); + } + + @GuardedBy("mService") + void dumpDebug(ProtoOutputStream proto, String dumpPackage) { + final int count = mUidObservers.getRegisteredCallbackCount(); + for (int i = 0; i < count; i++) { + final UidObserverRegistration reg = (UidObserverRegistration) + mUidObservers.getRegisteredCallbackCookie(i); + if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) { + reg.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS); + } + } + } + + private static final class UidObserverRegistration { + final int mUid; + final String mPkg; + final int mWhich; + final int mCutpoint; + + /** + * Total # of callback calls that took more than {@link #SLOW_UID_OBSERVER_THRESHOLD_MS}. + * We show it in dumpsys. + */ + int mSlowDispatchCount; + + /** Max time it took for each dispatch. */ + int mMaxDispatchTime; + + final SparseIntArray mLastProcStates; + + // Please keep the enum lists in sync + private static final int[] ORIG_ENUMS = new int[]{ + ActivityManager.UID_OBSERVER_IDLE, + ActivityManager.UID_OBSERVER_ACTIVE, + ActivityManager.UID_OBSERVER_GONE, + ActivityManager.UID_OBSERVER_PROCSTATE, + }; + private static final int[] PROTO_ENUMS = new int[]{ + ActivityManagerProto.UID_OBSERVER_FLAG_IDLE, + ActivityManagerProto.UID_OBSERVER_FLAG_ACTIVE, + ActivityManagerProto.UID_OBSERVER_FLAG_GONE, + ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE, + }; + + UidObserverRegistration(int uid, String pkg, int which, int cutpoint) { + this.mUid = uid; + this.mPkg = pkg; + this.mWhich = which; + this.mCutpoint = cutpoint; + if (cutpoint >= ActivityManager.MIN_PROCESS_STATE) { + mLastProcStates = new SparseIntArray(); + } else { + mLastProcStates = null; + } + } + + void dumpDebug(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + proto.write(UidObserverRegistrationProto.UID, mUid); + proto.write(UidObserverRegistrationProto.PACKAGE, mPkg); + ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, UidObserverRegistrationProto.FLAGS, + mWhich, ORIG_ENUMS, PROTO_ENUMS); + proto.write(UidObserverRegistrationProto.CUT_POINT, mCutpoint); + if (mLastProcStates != null) { + final int size = mLastProcStates.size(); + for (int i = 0; i < size; i++) { + final long pToken = proto.start(UidObserverRegistrationProto.LAST_PROC_STATES); + proto.write(UidObserverRegistrationProto.ProcState.UID, + mLastProcStates.keyAt(i)); + proto.write(UidObserverRegistrationProto.ProcState.STATE, + mLastProcStates.valueAt(i)); + proto.end(pToken); + } + } + proto.end(token); + } + } +} diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 0658e8139cc2..83bf28e2d3ba 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -988,6 +988,8 @@ class UserController implements Handler.Callback { final ArrayList<IStopUserCallback> stopCallbacks; final ArrayList<KeyEvictedCallback> keyEvictedCallbacks; int userIdToLock = userId; + // Must get a reference to UserInfo before it's removed + final UserInfo userInfo = getUserInfo(userId); synchronized (mLock) { stopCallbacks = new ArrayList<>(uss.mStopCallbacks); keyEvictedCallbacks = new ArrayList<>(uss.mKeyEvictedCallbacks); @@ -1030,8 +1032,8 @@ class UserController implements Handler.Callback { if (stopped) { mInjector.systemServiceManagerCleanupUser(userId); mInjector.stackSupervisorRemoveUser(userId); + // Remove the user if it is ephemeral. - UserInfo userInfo = getUserInfo(userId); if (userInfo.isEphemeral() && !userInfo.preCreated) { mInjector.getUserManager().removeUserEvenWhenDisallowed(userId); } @@ -1630,7 +1632,7 @@ class UserController implements Handler.Callback { UserInfo currentUserInfo = getUserInfo(currentUserId); Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo); mUiHandler.removeMessages(START_USER_SWITCH_UI_MSG); - mUiHandler.sendMessage(mHandler.obtainMessage( + mUiHandler.sendMessage(mUiHandler.obtainMessage( START_USER_SWITCH_UI_MSG, userNames)); } else { mHandler.removeMessages(START_USER_SWITCH_FG_MSG); @@ -2887,13 +2889,18 @@ class UserController implements Handler.Callback { void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser, String switchingFromSystemUserMessage, String switchingToSystemUserMessage) { - if (!mService.mContext.getPackageManager() + if (mService.mContext.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { - final Dialog d = new UserSwitchingDialog(mService, mService.mContext, fromUser, - toUser, true /* above system */, switchingFromSystemUserMessage, - switchingToSystemUserMessage); - d.show(); - } + // config_customUserSwitchUi is set to true on Automotive as CarSystemUI is + // responsible to show the UI; OEMs should not change that, but if they do, we + // should at least warn the user... + Slog.w(TAG, "Showing user switch dialog on UserController, it could cause a race " + + "condition if it's shown by CarSystemUI as well"); + } + final Dialog d = new UserSwitchingDialog(mService, mService.mContext, fromUser, + toUser, true /* above system */, switchingFromSystemUserMessage, + switchingToSystemUserMessage); + d.show(); } void reportGlobalUsageEventLocked(int event) { diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index ee441bf06d04..dfe8af155a04 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -6222,7 +6222,9 @@ public class AppOpsService extends IAppOpsService.Stub { int[] users; if (userId == UserHandle.USER_ALL) { - List<UserInfo> liveUsers = UserManager.get(mContext).getUsers(false); + // TODO(b/157921703): this call is returning all users, not just live ones - we + // need to either fix the method called, or rename the variable + List<UserInfo> liveUsers = UserManager.get(mContext).getUsers(); users = new int[liveUsers.size()]; for (int i = 0; i < liveUsers.size(); i++) { diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 2bbbbf1664d1..06ef58f8cc61 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -28,7 +28,7 @@ import android.media.AudioDeviceAttributes; import android.media.AudioRoutesInfo; import android.media.AudioSystem; import android.media.IAudioRoutesObserver; -import android.media.IStrategyPreferredDeviceDispatcher; +import android.media.IStrategyPreferredDevicesDispatcher; import android.media.MediaMetrics; import android.os.Binder; import android.os.Handler; @@ -47,6 +47,7 @@ import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -526,23 +527,23 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ int setPreferredDeviceForStrategySync(int strategy, - @NonNull AudioDeviceAttributes device) { - return mDeviceInventory.setPreferredDeviceForStrategySync(strategy, device); + /*package*/ int setPreferredDevicesForStrategySync(int strategy, + @NonNull List<AudioDeviceAttributes> devices) { + return mDeviceInventory.setPreferredDevicesForStrategySync(strategy, devices); } - /*package*/ int removePreferredDeviceForStrategySync(int strategy) { - return mDeviceInventory.removePreferredDeviceForStrategySync(strategy); + /*package*/ int removePreferredDevicesForStrategySync(int strategy) { + return mDeviceInventory.removePreferredDevicesForStrategySync(strategy); } - /*package*/ void registerStrategyPreferredDeviceDispatcher( - @NonNull IStrategyPreferredDeviceDispatcher dispatcher) { - mDeviceInventory.registerStrategyPreferredDeviceDispatcher(dispatcher); + /*package*/ void registerStrategyPreferredDevicesDispatcher( + @NonNull IStrategyPreferredDevicesDispatcher dispatcher) { + mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher); } - /*package*/ void unregisterStrategyPreferredDeviceDispatcher( - @NonNull IStrategyPreferredDeviceDispatcher dispatcher) { - mDeviceInventory.unregisterStrategyPreferredDeviceDispatcher(dispatcher); + /*package*/ void unregisterStrategyPreferredDevicesDispatcher( + @NonNull IStrategyPreferredDevicesDispatcher dispatcher) { + mDeviceInventory.unregisterStrategyPreferredDevicesDispatcher(dispatcher); } //--------------------------------------------------------------------- @@ -683,14 +684,14 @@ import java.util.concurrent.atomic.AtomicBoolean; sendLMsgNoDelay(MSG_L_SPEAKERPHONE_CLIENT_DIED, SENDMSG_QUEUE, obj); } - /*package*/ void postSaveSetPreferredDeviceForStrategy(int strategy, - AudioDeviceAttributes device) + /*package*/ void postSaveSetPreferredDevicesForStrategy(int strategy, + List<AudioDeviceAttributes> devices) { - sendILMsgNoDelay(MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY, SENDMSG_QUEUE, strategy, device); + sendILMsgNoDelay(MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy, devices); } - /*package*/ void postSaveRemovePreferredDeviceForStrategy(int strategy) { - sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY, SENDMSG_QUEUE, strategy); + /*package*/ void postSaveRemovePreferredDevicesForStrategy(int strategy) { + sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy); } //--------------------------------------------------------------------- @@ -1082,14 +1083,15 @@ import java.util.concurrent.atomic.AtomicBoolean; info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice); } } break; - case MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY: { + case MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY: { final int strategy = msg.arg1; - final AudioDeviceAttributes device = (AudioDeviceAttributes) msg.obj; - mDeviceInventory.onSaveSetPreferredDevice(strategy, device); + final List<AudioDeviceAttributes> devices = + (List<AudioDeviceAttributes>) msg.obj; + mDeviceInventory.onSaveSetPreferredDevices(strategy, devices); } break; - case MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY: { + case MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY: { final int strategy = msg.arg1; - mDeviceInventory.onSaveRemovePreferredDevice(strategy); + mDeviceInventory.onSaveRemovePreferredDevices(strategy); } break; case MSG_CHECK_MUTE_MUSIC: checkMessagesMuteMusic(0); @@ -1163,8 +1165,8 @@ import java.util.concurrent.atomic.AtomicBoolean; // a ScoClient died in BtHelper private static final int MSG_L_SCOCLIENT_DIED = 32; - private static final int MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY = 33; - private static final int MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY = 34; + private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY = 33; + private static final int MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY = 34; private static final int MSG_L_SPEAKERPHONE_CLIENT_DIED = 35; private static final int MSG_CHECK_MUTE_MUSIC = 36; diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 02a846e3dc82..fbf07cc591ff 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -16,7 +16,6 @@ package com.android.server.audio; import android.annotation.NonNull; -import android.annotation.Nullable; import android.app.ActivityManager; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; @@ -32,7 +31,7 @@ import android.media.AudioPort; import android.media.AudioRoutesInfo; import android.media.AudioSystem; import android.media.IAudioRoutesObserver; -import android.media.IStrategyPreferredDeviceDispatcher; +import android.media.IStrategyPreferredDevicesDispatcher; import android.media.MediaMetrics; import android.os.Binder; import android.os.RemoteCallbackList; @@ -51,6 +50,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.List; import java.util.Set; /** @@ -137,7 +137,8 @@ public class AudioDeviceInventory { private final ArrayMap<Integer, String> mApmConnectedDevices = new ArrayMap<>(); // List of preferred devices for strategies - private final ArrayMap<Integer, AudioDeviceAttributes> mPreferredDevices = new ArrayMap<>(); + private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevices = + new ArrayMap<>(); // the wrapper for AudioSystem static methods, allows us to spy AudioSystem private final @NonNull AudioSystemAdapter mAudioSystem; @@ -150,8 +151,8 @@ public class AudioDeviceInventory { new RemoteCallbackList<IAudioRoutesObserver>(); // Monitoring of strategy-preferred device - final RemoteCallbackList<IStrategyPreferredDeviceDispatcher> mPrefDevDispatchers = - new RemoteCallbackList<IStrategyPreferredDeviceDispatcher>(); + final RemoteCallbackList<IStrategyPreferredDevicesDispatcher> mPrefDevDispatchers = + new RemoteCallbackList<IStrategyPreferredDevicesDispatcher>(); /*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) { mDeviceBroker = broker; @@ -265,8 +266,9 @@ public class AudioDeviceInventory { } } synchronized (mPreferredDevices) { - mPreferredDevices.forEach((strategy, device) -> { - mAudioSystem.setPreferredDeviceForStrategy(strategy, device); }); + mPreferredDevices.forEach((strategy, devices) -> { + mAudioSystem.setDevicesRoleForStrategy( + strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); }); } } @@ -600,49 +602,52 @@ public class AudioDeviceInventory { mmi.record(); } - /*package*/ void onSaveSetPreferredDevice(int strategy, @NonNull AudioDeviceAttributes device) { - mPreferredDevices.put(strategy, device); - dispatchPreferredDevice(strategy, device); + /*package*/ void onSaveSetPreferredDevices(int strategy, + @NonNull List<AudioDeviceAttributes> devices) { + mPreferredDevices.put(strategy, devices); + dispatchPreferredDevice(strategy, devices); } - /*package*/ void onSaveRemovePreferredDevice(int strategy) { + /*package*/ void onSaveRemovePreferredDevices(int strategy) { mPreferredDevices.remove(strategy); - dispatchPreferredDevice(strategy, null); + dispatchPreferredDevice(strategy, new ArrayList<AudioDeviceAttributes>()); } //------------------------------------------------------------ // - /*package*/ int setPreferredDeviceForStrategySync(int strategy, - @NonNull AudioDeviceAttributes device) { + /*package*/ int setPreferredDevicesForStrategySync(int strategy, + @NonNull List<AudioDeviceAttributes> devices) { final long identity = Binder.clearCallingIdentity(); - final int status = mAudioSystem.setPreferredDeviceForStrategy(strategy, device); + final int status = mAudioSystem.setDevicesRoleForStrategy( + strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); Binder.restoreCallingIdentity(identity); if (status == AudioSystem.SUCCESS) { - mDeviceBroker.postSaveSetPreferredDeviceForStrategy(strategy, device); + mDeviceBroker.postSaveSetPreferredDevicesForStrategy(strategy, devices); } return status; } - /*package*/ int removePreferredDeviceForStrategySync(int strategy) { + /*package*/ int removePreferredDevicesForStrategySync(int strategy) { final long identity = Binder.clearCallingIdentity(); - final int status = mAudioSystem.removePreferredDeviceForStrategy(strategy); + final int status = mAudioSystem.removeDevicesRoleForStrategy( + strategy, AudioSystem.DEVICE_ROLE_PREFERRED); Binder.restoreCallingIdentity(identity); if (status == AudioSystem.SUCCESS) { - mDeviceBroker.postSaveRemovePreferredDeviceForStrategy(strategy); + mDeviceBroker.postSaveRemovePreferredDevicesForStrategy(strategy); } return status; } - /*package*/ void registerStrategyPreferredDeviceDispatcher( - @NonNull IStrategyPreferredDeviceDispatcher dispatcher) { + /*package*/ void registerStrategyPreferredDevicesDispatcher( + @NonNull IStrategyPreferredDevicesDispatcher dispatcher) { mPrefDevDispatchers.register(dispatcher); } - /*package*/ void unregisterStrategyPreferredDeviceDispatcher( - @NonNull IStrategyPreferredDeviceDispatcher dispatcher) { + /*package*/ void unregisterStrategyPreferredDevicesDispatcher( + @NonNull IStrategyPreferredDevicesDispatcher dispatcher) { mPrefDevDispatchers.unregister(dispatcher); } @@ -1288,11 +1293,13 @@ public class AudioDeviceInventory { } } - private void dispatchPreferredDevice(int strategy, @Nullable AudioDeviceAttributes device) { + private void dispatchPreferredDevice(int strategy, + @NonNull List<AudioDeviceAttributes> devices) { final int nbDispatchers = mPrefDevDispatchers.beginBroadcast(); for (int i = 0; i < nbDispatchers; i++) { try { - mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDeviceChanged(strategy, device); + mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDevicesChanged( + strategy, devices); } catch (RemoteException e) { } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index b94396634530..0a179e89f757 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -83,7 +83,7 @@ import android.media.IAudioService; import android.media.IPlaybackConfigDispatcher; import android.media.IRecordingConfigDispatcher; import android.media.IRingtonePlayer; -import android.media.IStrategyPreferredDeviceDispatcher; +import android.media.IStrategyPreferredDevicesDispatcher; import android.media.IVolumeController; import android.media.MediaExtractor; import android.media.MediaFormat; @@ -167,6 +167,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; /** * The implementation of the audio service for volume, audio focus, device management... @@ -1282,6 +1283,7 @@ public class AudioService extends IAudioService.Stub } if (isPlatformTelevision()) { + checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI, caller); synchronized (mHdmiClientLock) { if (mHdmiManager != null && mHdmiPlaybackClient != null) { updateHdmiCecSinkLocked(mHdmiCecSink | false); @@ -1301,54 +1303,22 @@ public class AudioService extends IAudioService.Stub } } - /** - * Update volume states for the given device. - * - * This will initialize the volume index if no volume index is available. - * If the device is the currently routed device, fixed/full volume policies will be applied. - * - * @param device a single audio device, ensure that this is not a devices bitmask - * @param caller caller of this method - */ - private void updateVolumeStatesForAudioDevice(int device, String caller) { + private void checkAddAllFixedVolumeDevices(int device, String caller) { final int numStreamTypes = AudioSystem.getNumStreamTypes(); for (int streamType = 0; streamType < numStreamTypes; streamType++) { - updateVolumeStates(device, streamType, caller); - } - } - - /** - * Update volume states for the given device and given stream. - * - * This will initialize the volume index if no volume index is available. - * If the device is the currently routed device, fixed/full volume policies will be applied. - * - * @param device a single audio device, ensure that this is not a devices bitmask - * @param streamType streamType to be updated - * @param caller caller of this method - */ - private void updateVolumeStates(int device, int streamType, String caller) { - if (!mStreamStates[streamType].hasIndexForDevice(device)) { - // set the default value, if device is affected by a full/fix/abs volume rule, it - // will taken into account in checkFixedVolumeDevices() - mStreamStates[streamType].setIndex( - mStreamStates[mStreamVolumeAlias[streamType]] - .getIndex(AudioSystem.DEVICE_OUT_DEFAULT), - device, caller, true /*hasModifyAudioSettings*/); - } - - // Check if device to be updated is routed for the given audio stream - List<AudioDeviceAttributes> devicesForAttributes = getDevicesForAttributes( - new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build()); - for (AudioDeviceAttributes deviceAttributes : devicesForAttributes) { - if (deviceAttributes.getType() == AudioDeviceInfo.convertInternalDeviceToDeviceType( - device)) { - mStreamStates[streamType].checkFixedVolumeDevices(); + if (!mStreamStates[streamType].hasIndexForDevice(device)) { + // set the default value, if device is affected by a full/fix/abs volume rule, it + // will taken into account in checkFixedVolumeDevices() + mStreamStates[streamType].setIndex( + mStreamStates[mStreamVolumeAlias[streamType]] + .getIndex(AudioSystem.DEVICE_OUT_DEFAULT), + device, caller, true /*hasModifyAudioSettings*/); + } + mStreamStates[streamType].checkFixedVolumeDevices(); - // Unmute streams if required if device is full volume - if (isStreamMute(streamType) && mFullVolumeDevices.contains(device)) { - mStreamStates[streamType].mute(false); - } + // Unmute streams if device is full volume + if (mFullVolumeDevices.contains(device)) { + mStreamStates[streamType].mute(false); } } } @@ -1808,22 +1778,28 @@ public class AudioService extends IAudioService.Stub /////////////////////////////////////////////////////////////////////////// // IPC methods /////////////////////////////////////////////////////////////////////////// - /** @see AudioManager#setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceInfo) */ - public int setPreferredDeviceForStrategy(int strategy, AudioDeviceAttributes device) { - if (device == null) { + /** + * @see AudioManager#setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes) + * @see AudioManager#setPreferredDevicesForStrategy(AudioProductStrategy, + * List<AudioDeviceAttributes>) + */ + public int setPreferredDevicesForStrategy(int strategy, List<AudioDeviceAttributes> devices) { + if (devices == null) { return AudioSystem.ERROR; } enforceModifyAudioRoutingPermission(); final String logString = String.format( "setPreferredDeviceForStrategy u/pid:%d/%d strat:%d dev:%s", - Binder.getCallingUid(), Binder.getCallingPid(), strategy, device.toString()); + Binder.getCallingUid(), Binder.getCallingPid(), strategy, + devices.stream().map(e -> e.toString()).collect(Collectors.joining(","))); sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG)); - if (device.getRole() == AudioDeviceAttributes.ROLE_INPUT) { + if (devices.stream().anyMatch(device -> + device.getRole() == AudioDeviceAttributes.ROLE_INPUT)) { Log.e(TAG, "Unsupported input routing in " + logString); return AudioSystem.ERROR; } - final int status = mDeviceBroker.setPreferredDeviceForStrategySync(strategy, device); + final int status = mDeviceBroker.setPreferredDevicesForStrategySync(strategy, devices); if (status != AudioSystem.SUCCESS) { Log.e(TAG, String.format("Error %d in %s)", status, logString)); } @@ -1832,53 +1808,61 @@ public class AudioService extends IAudioService.Stub } /** @see AudioManager#removePreferredDeviceForStrategy(AudioProductStrategy) */ - public int removePreferredDeviceForStrategy(int strategy) { + public int removePreferredDevicesForStrategy(int strategy) { enforceModifyAudioRoutingPermission(); final String logString = String.format("removePreferredDeviceForStrategy strat:%d", strategy); sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG)); - final int status = mDeviceBroker.removePreferredDeviceForStrategySync(strategy); + final int status = mDeviceBroker.removePreferredDevicesForStrategySync(strategy); if (status != AudioSystem.SUCCESS) { Log.e(TAG, String.format("Error %d in %s)", status, logString)); } return status; } - /** @see AudioManager#getPreferredDeviceForStrategy(AudioProductStrategy) */ - public AudioDeviceAttributes getPreferredDeviceForStrategy(int strategy) { + /** + * @see AudioManager#getPreferredDeviceForStrategy(AudioProductStrategy) + * @see AudioManager#getPreferredDevicesForStrategy(AudioProductStrategy) + */ + public List<AudioDeviceAttributes> getPreferredDevicesForStrategy(int strategy) { enforceModifyAudioRoutingPermission(); - AudioDeviceAttributes[] devices = new AudioDeviceAttributes[1]; + List<AudioDeviceAttributes> devices = new ArrayList<>(); final long identity = Binder.clearCallingIdentity(); - final int status = AudioSystem.getPreferredDeviceForStrategy(strategy, devices); + final int status = AudioSystem.getDevicesForRoleAndStrategy( + strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); Binder.restoreCallingIdentity(identity); if (status != AudioSystem.SUCCESS) { Log.e(TAG, String.format("Error %d in getPreferredDeviceForStrategy(%d)", status, strategy)); - return null; + return new ArrayList<AudioDeviceAttributes>(); } else { - return devices[0]; + return devices; } } - /** @see AudioManager#addOnPreferredDeviceForStrategyChangedListener(Executor, AudioManager.OnPreferredDeviceForStrategyChangedListener) */ - public void registerStrategyPreferredDeviceDispatcher( - @Nullable IStrategyPreferredDeviceDispatcher dispatcher) { + /** @see AudioManager#addOnPreferredDevicesForStrategyChangedListener( + * Executor, AudioManager.OnPreferredDevicesForStrategyChangedListener) + */ + public void registerStrategyPreferredDevicesDispatcher( + @Nullable IStrategyPreferredDevicesDispatcher dispatcher) { if (dispatcher == null) { return; } enforceModifyAudioRoutingPermission(); - mDeviceBroker.registerStrategyPreferredDeviceDispatcher(dispatcher); + mDeviceBroker.registerStrategyPreferredDevicesDispatcher(dispatcher); } - /** @see AudioManager#removeOnPreferredDeviceForStrategyChangedListener(AudioManager.OnPreferredDeviceForStrategyChangedListener) */ - public void unregisterStrategyPreferredDeviceDispatcher( - @Nullable IStrategyPreferredDeviceDispatcher dispatcher) { + /** @see AudioManager#removeOnPreferredDevicesForStrategyChangedListener( + * AudioManager.OnPreferredDevicesForStrategyChangedListener) + */ + public void unregisterStrategyPreferredDevicesDispatcher( + @Nullable IStrategyPreferredDevicesDispatcher dispatcher) { if (dispatcher == null) { return; } enforceModifyAudioRoutingPermission(); - mDeviceBroker.unregisterStrategyPreferredDeviceDispatcher(dispatcher); + mDeviceBroker.unregisterStrategyPreferredDevicesDispatcher(dispatcher); } /** @see AudioManager#getDevicesForAttributes(AudioAttributes) */ @@ -4932,15 +4916,7 @@ public class AudioService extends IAudioService.Stub synchronized (VolumeStreamState.class) { for (int stream = 0; stream < mStreamStates.length; stream++) { if (stream != skipStream) { - int devices = mStreamStates[stream].observeDevicesForStream_syncVSS( - false /*checkOthers*/); - - Set<Integer> devicesSet = AudioSystem.generateAudioDeviceTypesSet(devices); - for (Integer device : devicesSet) { - // Update volume states for devices routed for the stream - updateVolumeStates(device, stream, - "AudioService#observeDevicesForStreams"); - } + mStreamStates[stream].observeDevicesForStream_syncVSS(false /*checkOthers*/); } } } @@ -5009,7 +4985,7 @@ public class AudioService extends IAudioService.Stub + Integer.toHexString(audioSystemDeviceOut) + " from:" + caller)); // make sure we have a volume entry for this device, and that volume is updated according // to volume behavior - updateVolumeStatesForAudioDevice(audioSystemDeviceOut, "setDeviceVolumeBehavior:" + caller); + checkAddAllFixedVolumeDevices(audioSystemDeviceOut, "setDeviceVolumeBehavior:" + caller); } /** @@ -7231,9 +7207,10 @@ public class AudioService extends IAudioService.Stub // HDMI output removeAudioSystemDeviceOutFromFullVolumeDevices(AudioSystem.DEVICE_OUT_HDMI); } - updateVolumeStatesForAudioDevice(AudioSystem.DEVICE_OUT_HDMI, - "HdmiPlaybackClient.DisplayStatusCallback"); } + + checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI, + "HdmiPlaybackClient.DisplayStatusCallback"); } private class MyHdmiControlStatusChangeListenerCallback diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java index e60243fc481c..a0e1ca78a5e7 100644 --- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java +++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; import android.media.AudioDeviceAttributes; import android.media.AudioSystem; +import java.util.List; + /** * Provides an adapter to access functionality of the android.media.AudioSystem class for device * related functionality. @@ -77,22 +79,25 @@ public class AudioSystemAdapter { } /** - * Same as {@link AudioSystem#setPreferredDeviceForStrategy(int, AudioDeviceAttributes)} + * Same as {@link AudioSystem#setDevicesRoleForStrategy(int, int, List)} * @param strategy - * @param device + * @param role + * @param devices * @return */ - public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDeviceAttributes device) { - return AudioSystem.setPreferredDeviceForStrategy(strategy, device); + public int setDevicesRoleForStrategy(int strategy, int role, + @NonNull List<AudioDeviceAttributes> devices) { + return AudioSystem.setDevicesRoleForStrategy(strategy, role, devices); } /** - * Same as {@link AudioSystem#removePreferredDeviceForStrategy(int)} + * Same as {@link AudioSystem#removeDevicesRoleForStrategy(int, int)} * @param strategy + * @param role * @return */ - public int removePreferredDeviceForStrategy(int strategy) { - return AudioSystem.removePreferredDeviceForStrategy(strategy); + public int removeDevicesRoleForStrategy(int strategy, int role) { + return AudioSystem.removeDevicesRoleForStrategy(strategy, role); } /** diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index a47904c40cc3..54c179030929 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -259,17 +259,6 @@ public class AuthService extends SystemService { } @Override - public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException { - checkInternalPermission(); - final long identity = Binder.clearCallingIdentity(); - try { - mBiometricService.resetLockout(userId, hardwareAuthToken); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - @Override public long[] getAuthenticatorIds() throws RemoteException { // In this method, we're not checking whether the caller is permitted to use face // API because current authenticator ID is leaked (in a more contrived way) via Android diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 382bdbb246a1..3e0a40f9c288 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -638,19 +638,6 @@ public class BiometricService extends SystemService { } @Override // Binder call - public void resetLockout(int userId, byte[] hardwareAuthToken) { - checkInternalPermission(); - - try { - for (BiometricSensor sensor : mSensors) { - sensor.impl.resetLockout(userId, hardwareAuthToken); - } - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception", e); - } - } - - @Override // Binder call public long[] getAuthenticatorIds(int callingUserId) { checkInternalPermission(); diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java index ab48fdb0fd6e..73fc17aa26f1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java @@ -45,7 +45,7 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I private final PowerManager mPowerManager; private final VibrationEffect mSuccessVibrationEffect; private final VibrationEffect mErrorVibrationEffect; - private boolean mShouldSendErrorToClient; + private boolean mShouldSendErrorToClient = true; private boolean mAlreadyCancelled; /** @@ -85,11 +85,11 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I // case (success, failure, or error) is received from the HAL (e.g. versions of fingerprint // that do not handle lockout under the HAL. In these cases, ensure that the framework only // sends errors once per ClientMonitor. - if (!mShouldSendErrorToClient) { + if (mShouldSendErrorToClient) { logOnError(getContext(), errorCode, vendorCode, getTargetUserId()); try { if (getListener() != null) { - mShouldSendErrorToClient = true; + mShouldSendErrorToClient = false; getListener().onError(getSensorId(), getCookie(), errorCode, vendorCode); } } catch (RemoteException e) { @@ -98,7 +98,7 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I } if (finish) { - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } @@ -114,7 +114,7 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I } @Override - public void cancelWithoutStarting(@NonNull FinishCallback finishCallback) { + public void cancelWithoutStarting(@NonNull Callback callback) { final int errorCode = BiometricConstants.BIOMETRIC_ERROR_CANCELED; try { if (getListener() != null) { @@ -123,7 +123,7 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I } catch (RemoteException e) { Slog.w(TAG, "Failed to invoke sendError", e); } - finishCallback.onClientFinished(this, true /* success */); + callback.onClientFinished(this, true /* success */); } /** @@ -155,7 +155,7 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I } } catch (RemoteException e) { Slog.w(TAG, "Failed to invoke sendAcquired", e); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index cb2321f524f6..9128359250df 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -99,6 +99,14 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> return getCookie() != 0; } + public long getOperationId() { + return mOperationId; + } + + public boolean isRestricted() { + return mIsRestricted; + } + @Override protected boolean isCryptoOperation() { return mOperationId != 0; @@ -191,7 +199,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> } } catch (RemoteException e) { Slog.e(TAG, "Unable to notify listener, finishing", e); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } @@ -211,8 +219,8 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> * Start authentication */ @Override - public void start(@NonNull FinishCallback finishCallback) { - super.start(finishCallback); + public void start(@NonNull Callback callback) { + super.start(callback); final @LockoutTracker.LockoutMode int lockoutMode = mLockoutTracker.getLockoutModeForUser(getTargetUserId()); diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index 4f37dccea42e..588e865112c2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -85,7 +85,7 @@ public class BiometricScheduler { static final int STATE_WAITING_FOR_COOKIE = 4; /** - * The {@link ClientMonitor.FinishCallback} has been invoked and the client is finished. + * The {@link ClientMonitor.Callback} has been invoked and the client is finished. */ static final int STATE_FINISHED = 5; @@ -99,13 +99,13 @@ public class BiometricScheduler { @interface OperationState {} @NonNull final ClientMonitor<?> clientMonitor; - @Nullable final ClientMonitor.FinishCallback clientFinishCallback; + @Nullable final ClientMonitor.Callback mClientCallback; @OperationState int state; Operation(@NonNull ClientMonitor<?> clientMonitor, - @Nullable ClientMonitor.FinishCallback finishCallback) { + @Nullable ClientMonitor.Callback callback) { this.clientMonitor = clientMonitor; - this.clientFinishCallback = finishCallback; + this.mClientCallback = callback; state = STATE_WAITING_IN_QUEUE; } @@ -133,7 +133,7 @@ public class BiometricScheduler { public void run() { if (operation.state != Operation.STATE_FINISHED) { Slog.e(tag, "[Watchdog Triggered]: " + operation); - operation.clientMonitor.mFinishCallback + operation.clientMonitor.mCallback .onClientFinished(operation.clientMonitor, false /* success */); } } @@ -175,17 +175,25 @@ public class BiometricScheduler { @Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; @NonNull private final IBiometricService mBiometricService; @NonNull private final Handler mHandler = new Handler(Looper.getMainLooper()); - @NonNull private final InternalFinishCallback mInternalFinishCallback; + @NonNull private final InternalCallback mInternalCallback; @NonNull private final Queue<Operation> mPendingOperations; @Nullable private Operation mCurrentOperation; @NonNull private final ArrayDeque<CrashState> mCrashStates; - // Internal finish callback, notified when an operation is complete. Notifies the requester + // Internal callback, notified when an operation is complete. Notifies the requester // that the operation is complete, before performing internal scheduler work (such as // starting the next client). - private class InternalFinishCallback implements ClientMonitor.FinishCallback { + public class InternalCallback implements ClientMonitor.Callback { @Override - public void onClientFinished(ClientMonitor<?> clientMonitor, boolean success) { + public void onClientStarted(@NonNull ClientMonitor<?> clientMonitor) { + Slog.d(getTag(), "[Started] " + clientMonitor); + if (mCurrentOperation.mClientCallback != null) { + mCurrentOperation.mClientCallback.onClientStarted(clientMonitor); + } + } + + @Override + public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, boolean success) { mHandler.post(() -> { if (mCurrentOperation == null) { Slog.e(getTag(), "[Finishing] " + clientMonitor @@ -203,8 +211,8 @@ public class BiometricScheduler { Slog.d(getTag(), "[Finishing] " + clientMonitor + ", success: " + success); mCurrentOperation.state = Operation.STATE_FINISHED; - if (mCurrentOperation.clientFinishCallback != null) { - mCurrentOperation.clientFinishCallback.onClientFinished(clientMonitor, success); + if (mCurrentOperation.mClientCallback != null) { + mCurrentOperation.mClientCallback.onClientFinished(clientMonitor, success); } if (mGestureAvailabilityDispatcher != null) { @@ -227,7 +235,7 @@ public class BiometricScheduler { public BiometricScheduler(@NonNull String tag, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { mBiometricTag = tag; - mInternalFinishCallback = new InternalFinishCallback(); + mInternalCallback = new InternalCallback(); mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher; mPendingOperations = new ArrayDeque<>(); mBiometricService = IBiometricService.Stub.asInterface( @@ -235,6 +243,14 @@ public class BiometricScheduler { mCrashStates = new ArrayDeque<>(); } + /** + * @return A reference to the internal callback that should be invoked whenever the scheduler + * needs to (e.g. client started, client finished). + */ + @NonNull protected InternalCallback getInternalCallback() { + return mInternalCallback; + } + private String getTag() { return BASE_TAG + "/" + mBiometricTag; } @@ -262,7 +278,7 @@ public class BiometricScheduler { } final Interruptable interruptable = (Interruptable) currentClient; - interruptable.cancelWithoutStarting(mInternalFinishCallback); + interruptable.cancelWithoutStarting(getInternalCallback()); // Now we wait for the client to send its FinishCallback, which kicks off the next // operation. return; @@ -280,7 +296,7 @@ public class BiometricScheduler { final boolean shouldStartNow = currentClient.getCookie() == 0; if (shouldStartNow) { Slog.d(getTag(), "[Starting] " + mCurrentOperation); - currentClient.start(mInternalFinishCallback); + currentClient.start(getInternalCallback()); mCurrentOperation.state = Operation.STATE_STARTED; } else { try { @@ -324,7 +340,7 @@ public class BiometricScheduler { Slog.d(getTag(), "[Starting] Prepared client: " + mCurrentOperation); mCurrentOperation.state = Operation.STATE_STARTED; - mCurrentOperation.clientMonitor.start(mInternalFinishCallback); + mCurrentOperation.clientMonitor.start(getInternalCallback()); } /** @@ -340,11 +356,11 @@ public class BiometricScheduler { * Adds a {@link ClientMonitor} to the pending queue * * @param clientMonitor operation to be scheduled - * @param clientFinishCallback optional callback, invoked when the client is finished, but + * @param clientCallback optional callback, invoked when the client is finished, but * before it has been removed from the queue. */ public void scheduleClientMonitor(@NonNull ClientMonitor<?> clientMonitor, - @Nullable ClientMonitor.FinishCallback clientFinishCallback) { + @Nullable ClientMonitor.Callback clientCallback) { // Mark any interruptable pending clients as canceling. Once they reach the head of the // queue, the scheduler will send ERROR_CANCELED and skip the operation. for (Operation operation : mPendingOperations) { @@ -356,7 +372,7 @@ public class BiometricScheduler { } } - mPendingOperations.add(new Operation(clientMonitor, clientFinishCallback)); + mPendingOperations.add(new Operation(clientMonitor, clientCallback)); Slog.d(getTag(), "[Added] " + clientMonitor + ", new queue size: " + mPendingOperations.size()); diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java index 8b27781940ac..dec40e39fb29 100644 --- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java @@ -42,7 +42,15 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde /** * Interface that ClientMonitor holders should use to receive callbacks. */ - public interface FinishCallback { + public interface Callback { + /** + * Invoked when the ClientMonitor operation has been started (e.g. reached the head of + * the queue and becomes the current operation). + * + * @param clientMonitor Reference of the ClientMonitor that is starting. + */ + default void onClientStarted(@NonNull ClientMonitor<?> clientMonitor) {} + /** * Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous * (i.e. Authenticate, Enroll, Enumerate, Remove) and synchronous (i.e. generateChallenge, @@ -52,7 +60,7 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde * @param clientMonitor Reference of the ClientMonitor that finished. * @param success True if the operation completed successfully. */ - void onClientFinished(ClientMonitor<?> clientMonitor, boolean success); + default void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, boolean success) {} } /** @@ -79,7 +87,7 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde private final int mCookie; boolean mAlreadyDone; - @NonNull protected FinishCallback mFinishCallback; + @NonNull protected Callback mCallback; /** * @param context system_server context @@ -125,17 +133,18 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde /** * Invoked if the scheduler is unable to start the ClientMonitor (for example the HAL is null). * If such a problem is detected, the scheduler will not invoke - * {@link #start(FinishCallback)}. + * {@link #start(Callback)}. */ public abstract void unableToStart(); /** * Starts the ClientMonitor's lifecycle. Invokes {@link #startHalOperation()} when internal book * keeping is complete. - * @param finishCallback invoked when the operation is complete (succeeds, fails, etc) + * @param callback invoked when the operation is complete (succeeds, fails, etc) */ - public void start(@NonNull FinishCallback finishCallback) { - mFinishCallback = finishCallback; + public void start(@NonNull Callback callback) { + mCallback = callback; + mCallback.onClientStarted(this); } /** diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java index 3cef6667b644..cb7db92ee132 100644 --- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java +++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java @@ -128,11 +128,11 @@ public final class ClientMonitorCallbackConverter { } } - public void onChallengeGenerated(long challenge) throws RemoteException { + public void onChallengeGenerated(int sensorId, long challenge) throws RemoteException { if (mFaceServiceReceiver != null) { - mFaceServiceReceiver.onChallengeGenerated(challenge); + mFaceServiceReceiver.onChallengeGenerated(sensorId, challenge); } else if (mFingerprintServiceReceiver != null) { - mFingerprintServiceReceiver.onChallengeGenerated(challenge); + mFingerprintServiceReceiver.onChallengeGenerated(sensorId, challenge); } } @@ -142,10 +142,21 @@ public final class ClientMonitorCallbackConverter { } } - public void onFeatureGet(boolean success, int feature, boolean value) - throws RemoteException { + public void onFeatureGet(boolean success, int feature, boolean value) throws RemoteException { if (mFaceServiceReceiver != null) { mFaceServiceReceiver.onFeatureGet(success, feature, value); } } + + public void onChallengeInterrupted(int sensorId) throws RemoteException { + if (mFaceServiceReceiver != null) { + mFaceServiceReceiver.onChallengeInterrupted(sensorId); + } + } + + public void onChallengeInterruptFinished(int sensorId) throws RemoteException { + if (mFaceServiceReceiver != null) { + mFaceServiceReceiver.onChallengeInterruptFinished(sensorId); + } + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java index add5829c1342..8bf9680d60cd 100644 --- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java @@ -77,18 +77,18 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> { mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier); logOnEnrolled(getTargetUserId(), System.currentTimeMillis() - mEnrollmentStartTimeMs, true /* enrollSuccessful */); - mFinishCallback.onClientFinished(this, true /* success */); + mCallback.onClientFinished(this, true /* success */); } notifyUserActivity(); } @Override - public void start(@NonNull FinishCallback finishCallback) { - super.start(finishCallback); + public void start(@NonNull Callback callback) { + super.start(callback); if (hasReachedEnrollmentLimit()) { Slog.e(TAG, "Reached enrollment limit"); - finishCallback.onClientFinished(this, false /* success */); + callback.onClientFinished(this, false /* success */); return; } diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java index dad5cad1ed8d..92c498c55dbf 100644 --- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java @@ -40,23 +40,23 @@ public abstract class GenerateChallengeClient<T> extends ClientMonitor<T> { @Override public void unableToStart() { try { - getListener().onChallengeGenerated(0L); + getListener().onChallengeGenerated(getSensorId(), 0L); } catch (RemoteException e) { Slog.e(TAG, "Unable to send error", e); } } @Override - public void start(@NonNull FinishCallback finishCallback) { - super.start(finishCallback); + public void start(@NonNull Callback callback) { + super.start(callback); startHalOperation(); try { - getListener().onChallengeGenerated(mChallenge); - mFinishCallback.onClientFinished(this, true /* success */); + getListener().onChallengeGenerated(getSensorId(), mChallenge); + mCallback.onClientFinished(this, true /* success */); } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java index 6d7b0fd3d5f1..5c08bce9b44f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java @@ -62,29 +62,35 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide private final List<S> mEnrolledList; private ClientMonitor<T> mCurrentTask; - private final FinishCallback mEnumerateFinishCallback = (clientMonitor, success) -> { - final List<BiometricAuthenticator.Identifier> unknownHALTemplates = - ((InternalEnumerateClient<T>) mCurrentTask).getUnknownHALTemplates(); - - if (!unknownHALTemplates.isEmpty()) { - Slog.w(TAG, "Adding " + unknownHALTemplates.size() + " templates for deletion"); - } - for (BiometricAuthenticator.Identifier unknownHALTemplate : unknownHALTemplates) { - mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplate, - mCurrentTask.getTargetUserId())); - } - - if (mUnknownHALTemplates.isEmpty()) { - // No unknown HAL templates. Unknown framework templates are already cleaned up in - // InternalEnumerateClient. Finish this client. - mFinishCallback.onClientFinished(this, success); - } else { - startCleanupUnknownHalTemplates(); + private final Callback mEnumerateCallback = new Callback() { + @Override + public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, boolean success) { + final List<BiometricAuthenticator.Identifier> unknownHALTemplates = + ((InternalEnumerateClient<T>) mCurrentTask).getUnknownHALTemplates(); + + if (!unknownHALTemplates.isEmpty()) { + Slog.w(TAG, "Adding " + unknownHALTemplates.size() + " templates for deletion"); + } + for (BiometricAuthenticator.Identifier unknownHALTemplate : unknownHALTemplates) { + mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplate, + mCurrentTask.getTargetUserId())); + } + + if (mUnknownHALTemplates.isEmpty()) { + // No unknown HAL templates. Unknown framework templates are already cleaned up in + // InternalEnumerateClient. Finish this client. + mCallback.onClientFinished(InternalCleanupClient.this, success); + } else { + startCleanupUnknownHalTemplates(); + } } }; - private final FinishCallback mRemoveFinishCallback = (clientMonitor, success) -> { - mFinishCallback.onClientFinished(this, success); + private final Callback mRemoveCallback = new Callback() { + @Override + public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, boolean success) { + mCallback.onClientFinished(InternalCleanupClient.this, success); + } }; protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context, @@ -116,7 +122,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, mStatsModality, BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL); - mCurrentTask.start(mRemoveFinishCallback); + mCurrentTask.start(mRemoveCallback); } @Override @@ -125,13 +131,13 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide } @Override - public void start(@NonNull FinishCallback finishCallback) { - super.start(finishCallback); + public void start(@NonNull Callback callback) { + super.start(callback); // Start enumeration. Removal will start if necessary, when enumeration is completed. mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(), getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId()); - mCurrentTask.start(mEnumerateFinishCallback); + mCurrentTask.start(mEnumerateCallback); } @Override @@ -147,7 +153,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide + mCurrentTask.getClass().getSimpleName()); return; } - ((RemovalClient) mCurrentTask).onRemoved(identifier, remaining); + ((RemovalClient<T>) mCurrentTask).onRemoved(identifier, remaining); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java index 3f73cd56e6c3..e07f71298d13 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java @@ -62,7 +62,7 @@ public abstract class InternalEnumerateClient<T> extends ClientMonitor<T> handleEnumeratedTemplate(identifier); if (remaining == 0) { doTemplateCleanup(); - mFinishCallback.onClientFinished(this, true /* success */); + mCallback.onClientFinished(this, true /* success */); } } @@ -72,8 +72,8 @@ public abstract class InternalEnumerateClient<T> extends ClientMonitor<T> } @Override - public void start(@NonNull FinishCallback finishCallback) { - super.start(finishCallback); + public void start(@NonNull Callback callback) { + super.start(callback); // The biometric template ids will be removed when we get confirmation from the HAL startHalOperation(); diff --git a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java index 35d917747574..28e0117afd36 100644 --- a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java +++ b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java @@ -37,10 +37,10 @@ public interface Interruptable { /** * Notifies the client that it needs to finish before - * {@link ClientMonitor#start(ClientMonitor.FinishCallback)} was invoked. This usually happens + * {@link ClientMonitor#start(ClientMonitor.Callback)} was invoked. This usually happens * if the client is still waiting in the pending queue and got notified that a subsequent * operation is preempting it. - * @param finishCallback invoked when the operation is completed. + * @param callback invoked when the operation is completed. */ - void cancelWithoutStarting(@NonNull ClientMonitor.FinishCallback finishCallback); + void cancelWithoutStarting(@NonNull ClientMonitor.Callback callback); } diff --git a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java index 1a4216f9d43c..d85ab25cc812 100644 --- a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java @@ -58,6 +58,10 @@ public abstract class LoggableMonitor { mStatsClient = statsClient; } + public int getStatsClient() { + return mStatsClient; + } + private boolean isAnyFieldUnknown() { return mStatsModality == BiometricsProtoEnums.MODALITY_UNKNOWN || mStatsAction == BiometricsProtoEnums.ACTION_UNKNOWN diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java index 1c49bcdbadf4..1348f797a2dc 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java @@ -55,8 +55,8 @@ public abstract class RemovalClient<T> extends ClientMonitor<T> implements Remov } @Override - public void start(@NonNull FinishCallback finishCallback) { - super.start(finishCallback); + public void start(@NonNull Callback callback) { + super.start(callback); // The biometric template ids will be removed when we get confirmation from the HAL startHalOperation(); @@ -85,7 +85,7 @@ public abstract class RemovalClient<T> extends ClientMonitor<T> implements Remov // cleanup). mAuthenticatorIds.put(getTargetUserId(), 0L); } - mFinishCallback.onClientFinished(this, true /* success */); + mCallback.onClientFinished(this, true /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java index b78ee49826f2..5deb8fa26639 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java @@ -36,10 +36,10 @@ public abstract class RevokeChallengeClient<T> extends ClientMonitor<T> { } @Override - public void start(@NonNull FinishCallback finishCallback) { - super.start(finishCallback); + public void start(@NonNull Callback callback) { + super.start(callback); startHalOperation(); - mFinishCallback.onClientFinished(this, true /* success */); + mCallback.onClientFinished(this, true /* success */); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java index 6c57208c1e84..32bb2db77ddc 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java @@ -29,6 +29,7 @@ import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback; import android.hardware.face.Face; +import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorProperties; import android.hardware.face.IFaceServiceReceiver; import android.os.Build; @@ -44,6 +45,7 @@ import android.os.UserManager; import android.provider.Settings; import android.util.Slog; +import com.android.internal.R; import com.android.internal.util.FrameworkStatsLog; import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.AcquisitionClient; @@ -98,6 +100,11 @@ class Face10 implements IHwBinder.DeathRecipient { @Nullable private IBiometricsFace mDaemon; private int mCurrentUserId = UserHandle.USER_NULL; + // If a challenge is generated, keep track of its owner. Since IBiometricsFace@1.0 only + // supports a single in-flight challenge, we must notify the interrupted owner that its + // challenge is no longer valid. The interrupted owner will be notified when the interrupter + // has finished. + @Nullable private FaceGenerateChallengeClient mCurrentChallengeOwner; private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() { @Override @@ -269,7 +276,10 @@ class Face10 implements IHwBinder.DeathRecipient { Face10(@NonNull Context context, int sensorId, @NonNull LockoutResetDispatcher lockoutResetDispatcher) { - mFaceSensorProperties = new FaceSensorProperties(sensorId, false /* supportsFaceDetect */); + final boolean supportsSelfIllumination = context.getResources() + .getBoolean(R.bool.config_faceAuthSupportsSelfIllumination); + mFaceSensorProperties = new FaceSensorProperties(sensorId, false /* supportsFaceDetect */, + supportsSelfIllumination); mContext = context; mSensorId = sensorId; mScheduler = new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */); @@ -394,9 +404,12 @@ class Face10 implements IHwBinder.DeathRecipient { final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId, mCurrentUserId, hasEnrolled, mAuthenticatorIds); - mScheduler.scheduleClientMonitor(client, (clientMonitor, success) -> { - if (success) { - mCurrentUserId = targetUserId; + mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() { + @Override + public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, boolean success) { + if (success) { + mCurrentUserId = targetUserId; + } } }); } @@ -456,21 +469,87 @@ class Face10 implements IHwBinder.DeathRecipient { }); } + /** + * {@link IBiometricsFace} only supports a single in-flight challenge. In cases where two + * callers both need challenges (e.g. resetLockout right before enrollment), we need to ensure + * that either: + * 1) generateChallenge/operation/revokeChallenge is complete before the next generateChallenge + * is processed by the scheduler, or + * 2) the generateChallenge callback provides a mechanism for notifying the caller that its + * challenge has been invalidated by a subsequent caller, as well as a mechanism for + * notifying the previous caller that the interrupting operation is complete (e.g. the + * interrupting client's challenge has been revoked, so that the interrupted client can + * start retry logic if necessary). See + * {@link FaceManager.GenerateChallengeCallback#onChallengeInterruptFinished(int)} + * The only case of conflicting challenges is currently resetLockout --> enroll. So, the second + * option seems better as it prioritizes the new operation, which is user-facing. + */ void scheduleGenerateChallenge(@NonNull IBinder token, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) { mHandler.post(() -> { + if (mCurrentChallengeOwner != null) { + Slog.w(TAG, "Current challenge owner: " + mCurrentChallengeOwner + + ", interrupted by: " + opPackageName); + try { + mCurrentChallengeOwner.getListener().onChallengeInterrupted(mSensorId); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to notify challenge interrupted", e); + } + } + final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName, - mSensorId); - mScheduler.scheduleClientMonitor(client); + mSensorId, mCurrentChallengeOwner); + mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() { + @Override + public void onClientStarted(@NonNull ClientMonitor<?> clientMonitor) { + if (client != clientMonitor) { + Slog.e(TAG, "scheduleGenerateChallenge, mismatched client." + + " Expecting: " + client + ", received: " + clientMonitor); + return; + } + Slog.d(TAG, "Current challenge owner: " + client); + mCurrentChallengeOwner = client; + } + }); }); } void scheduleRevokeChallenge(@NonNull IBinder token, @NonNull String owner) { mHandler.post(() -> { + if (!mCurrentChallengeOwner.getOwnerString().contentEquals(owner)) { + Slog.e(TAG, "scheduleRevokeChallenge, package: " + owner + + " attempting to revoke challenge owned by: " + + mCurrentChallengeOwner.getOwnerString()); + return; + } + final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext, mLazyDaemon, token, owner, mSensorId); - mScheduler.scheduleClientMonitor(client); + mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() { + @Override + public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, + boolean success) { + if (client != clientMonitor) { + Slog.e(TAG, "scheduleRevokeChallenge, mismatched client." + + "Expecting: " + client + ", received: " + clientMonitor); + return; + } + + final FaceGenerateChallengeClient previousChallengeOwner = + mCurrentChallengeOwner.getInterruptedClient(); + mCurrentChallengeOwner = null; + Slog.d(TAG, "Previous challenge owner: " + previousChallengeOwner); + if (previousChallengeOwner != null) { + try { + previousChallengeOwner.getListener() + .onChallengeInterruptFinished(mSensorId); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to notify interrupt finished", e); + } + } + } + }); }); } @@ -488,12 +567,16 @@ class Face10 implements IHwBinder.DeathRecipient { opPackageName, FaceUtils.getInstance(), disabledFeatures, ENROLL_TIMEOUT_SEC, surfaceHandle, mSensorId); - mScheduler.scheduleClientMonitor(client, ((clientMonitor, success) -> { - if (success) { - // Update authenticatorIds - scheduleUpdateActiveUserWithoutHandler(client.getTargetUserId()); + mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() { + @Override + public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, + boolean success) { + if (success) { + // Update authenticatorIds + scheduleUpdateActiveUserWithoutHandler(client.getTargetUserId()); + } } - })); + }); }); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java index 21bda74bc6b9..892d6a48488d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java @@ -92,7 +92,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting auth", e); onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } @@ -103,7 +103,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting cancel", e); onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } @@ -131,7 +131,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { // 1) Authenticated == true // 2) Error occurred // 3) Authenticated == false - mFinishCallback.onClientFinished(this, true /* success */); + mCallback.onClientFinished(this, true /* success */); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java index 33244b8e61a8..bbee6cde4603 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java @@ -75,11 +75,6 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub { } @Override - public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException { - mFaceService.resetLockout(userId, hardwareAuthToken); - } - - @Override public long getAuthenticatorId(int callingUserId) throws RemoteException { return mFaceService.getAuthenticatorId(callingUserId); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java index 52a822675c2f..3e843cae96fd 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java @@ -116,12 +116,12 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> { } if (status != Status.OK) { onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting enroll", e); onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } @@ -132,7 +132,7 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> { } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting cancel", e); onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java index 67f2712d0b9d..ba401f255e43 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java @@ -17,12 +17,14 @@ package com.android.server.biometrics.sensors.face; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.sensors.ClientMonitor; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.GenerateChallengeClient; @@ -36,10 +38,22 @@ public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiomet private static final String TAG = "FaceGenerateChallengeClient"; private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes + // If `this` FaceGenerateChallengeClient was invoked while an existing in-flight challenge + // was not revoked yet, store a reference to the interrupted client here. Notify the interrupted + // client when `this` challenge is revoked. + @Nullable private final FaceGenerateChallengeClient mInterruptedClient; + FaceGenerateChallengeClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token, - @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) { + @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId, + @Nullable FaceGenerateChallengeClient interruptedClient) { super(context, lazyDaemon, token, listener, owner, sensorId); + mInterruptedClient = interruptedClient; + } + + @Nullable + public FaceGenerateChallengeClient getInterruptedClient() { + return mInterruptedClient; } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java index ce57cb7686ed..8c7b99d6eb52 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java @@ -61,8 +61,8 @@ public class FaceGetFeatureClient extends ClientMonitor<IBiometricsFace> { } @Override - public void start(@NonNull FinishCallback finishCallback) { - super.start(finishCallback); + public void start(@NonNull Callback callback) { + super.start(callback); startHalOperation(); } @@ -71,10 +71,10 @@ public class FaceGetFeatureClient extends ClientMonitor<IBiometricsFace> { try { final OptionalBool result = getFreshDaemon().getFeature(mFeature, mFaceId); getListener().onFeatureGet(result.status == Status.OK, mFeature, result.value); - mFinishCallback.onClientFinished(this, true /* success */); + mCallback.onClientFinished(this, true /* success */); } catch (RemoteException e) { Slog.e(TAG, "Unable to getFeature", e); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java index f25242ee9b85..32d48321428a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java @@ -52,7 +52,7 @@ class FaceInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFac getFreshDaemon().enumerate(); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting enumerate", e); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java index 00d5f500b241..dde5ababd6c0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java @@ -51,7 +51,7 @@ class FaceRemovalClient extends RemovalClient<IBiometricsFace> { getFreshDaemon().remove(mBiometricId); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting remove", e); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java index 69070da0491e..f4324bedb4c6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java @@ -56,8 +56,8 @@ public class FaceResetLockoutClient extends ClientMonitor<IBiometricsFace> { } @Override - public void start(@NonNull FinishCallback finishCallback) { - super.start(finishCallback); + public void start(@NonNull Callback callback) { + super.start(callback); startHalOperation(); } @@ -65,10 +65,10 @@ public class FaceResetLockoutClient extends ClientMonitor<IBiometricsFace> { protected void startHalOperation() { try { getFreshDaemon().resetLockout(mHardwareAuthToken); - mFinishCallback.onClientFinished(this, true /* success */); + mCallback.onClientFinished(this, true /* success */); } catch (RemoteException e) { Slog.e(TAG, "Unable to reset lockout", e); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index b832a09f8f88..b689106bbc44 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -80,16 +80,27 @@ public class FaceService extends SystemService { } @Override // Binder call - public void generateChallenge(IBinder token, IFaceServiceReceiver receiver, + public void generateChallenge(IBinder token, int sensorId, IFaceServiceReceiver receiver, String opPackageName) { Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); - mFace10.scheduleGenerateChallenge(token, receiver, opPackageName); + if (sensorId == mFace10.getFaceSensorProperties().sensorId) { + mFace10.scheduleGenerateChallenge(token, receiver, opPackageName); + return; + } + + Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId); } @Override // Binder call - public void revokeChallenge(IBinder token, String owner) { + public void revokeChallenge(IBinder token, int sensorId, String owner) { Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); - mFace10.scheduleRevokeChallenge(token, owner); + + if (sensorId == mFace10.getFaceSensorProperties().sensorId) { + mFace10.scheduleRevokeChallenge(token, owner); + return; + } + + Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId); } @Override // Binder call @@ -267,9 +278,16 @@ public class FaceService extends SystemService { } @Override // Binder call - public void resetLockout(int userId, byte[] hardwareAuthToken) { + public void resetLockout(IBinder token, int sensorId, int userId, byte[] hardwareAuthToken, + String opPackageName) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); - mFace10.scheduleResetLockout(userId, hardwareAuthToken); + + if (sensorId == mFace10.getFaceSensorProperties().sensorId) { + mFace10.scheduleResetLockout(userId, hardwareAuthToken); + return; + } + + Slog.w(TAG, "No matching sensor for resetLockout, sensorId: " + sensorId); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java index e7d041a11ccb..94abb7f378df 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java @@ -70,8 +70,8 @@ public class FaceSetFeatureClient extends ClientMonitor<IBiometricsFace> { } @Override - public void start(@NonNull FinishCallback finishCallback) { - super.start(finishCallback); + public void start(@NonNull Callback callback) { + super.start(callback); startHalOperation(); } @@ -82,10 +82,10 @@ public class FaceSetFeatureClient extends ClientMonitor<IBiometricsFace> { final int result = getFreshDaemon() .setFeature(mFeature, mEnabled, mHardwareAuthToken, mFaceId); getListener().onFeatureSet(result == Status.OK, mFeature); - mFinishCallback.onClientFinished(this, true /* success */); + mCallback.onClientFinished(this, true /* success */); } catch (RemoteException e) { Slog.e(TAG, "Unable to set feature: " + mFeature + " to enabled: " + mEnabled, e); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java index bcf304e47816..05b176d28e28 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java @@ -50,8 +50,8 @@ public class FaceUpdateActiveUserClient extends ClientMonitor<IBiometricsFace> { } @Override - public void start(@NonNull FinishCallback finishCallback) { - super.start(finishCallback); + public void start(@NonNull Callback callback) { + super.start(callback); if (mCurrentUserId == getTargetUserId()) { Slog.d(TAG, "Already user: " + mCurrentUserId + ", refreshing authenticatorId"); @@ -61,7 +61,7 @@ public class FaceUpdateActiveUserClient extends ClientMonitor<IBiometricsFace> { } catch (RemoteException e) { Slog.e(TAG, "Unable to refresh authenticatorId", e); } - finishCallback.onClientFinished(this, true /* success */); + callback.onClientFinished(this, true /* success */); return; } @@ -79,16 +79,16 @@ public class FaceUpdateActiveUserClient extends ClientMonitor<IBiometricsFace> { FACE_DATA_DIR); if (!storePath.exists()) { Slog.e(TAG, "vold has not created the directory?"); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); return; } try { getFreshDaemon().setActiveUser(getTargetUserId(), storePath.getAbsolutePath()); - mFinishCallback.onClientFinished(this, true /* success */); + mCallback.onClientFinished(this, true /* success */); } catch (RemoteException e) { Slog.e(TAG, "Failed to setActiveUser: " + e); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java index dad038626762..c5c28227fd24 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java @@ -84,7 +84,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient { private static final String TAG = "Fingerprint21"; private static final int ENROLL_TIMEOUT_SEC = 60; - private final Context mContext; + final Context mContext; private final IActivityTaskManager mActivityTaskManager; private final FingerprintSensorProperties mSensorProperties; private final BiometricScheduler mScheduler; @@ -96,6 +96,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient { private final Map<Integer, Long> mAuthenticatorIds; @Nullable private IBiometricsFingerprint mDaemon; + @NonNull private final HalResultController mHalResultController; @Nullable private IUdfpsOverlayController mUdfpsOverlayController; private int mCurrentUserId = UserHandle.USER_NULL; @@ -146,15 +147,37 @@ class Fingerprint21 implements IHwBinder.DeathRecipient { } }; - private final IBiometricsFingerprintClientCallback mDaemonCallback = - new IBiometricsFingerprintClientCallback.Stub() { + public static class HalResultController extends IBiometricsFingerprintClientCallback.Stub { + + /** + * Interface to sends results to the HalResultController's owner. + */ + public interface Callback { + /** + * Invoked when the HAL sends ERROR_HW_UNAVAILABLE. + */ + void onHardwareUnavailable(); + } + + @NonNull private final Context mContext; + @NonNull final Handler mHandler; + @NonNull final BiometricScheduler mScheduler; + @Nullable private Callback mCallback; + + HalResultController(@NonNull Context context, @NonNull Handler handler, + @NonNull BiometricScheduler scheduler) { + mContext = context; + mHandler = handler; + mScheduler = scheduler; + } + + public void setCallback(@Nullable Callback callback) { + mCallback = callback; + } + @Override public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) { mHandler.post(() -> { - final CharSequence name = FingerprintUtils.getInstance() - .getUniqueName(mContext, mCurrentUserId); - final Fingerprint fingerprint = new Fingerprint(name, groupId, fingerId, deviceId); - final ClientMonitor<?> client = mScheduler.getCurrentClient(); if (!(client instanceof FingerprintEnrollClient)) { Slog.e(TAG, "onEnrollResult for non-enroll client: " @@ -162,6 +185,11 @@ class Fingerprint21 implements IHwBinder.DeathRecipient { return; } + final int currentUserId = client.getTargetUserId(); + final CharSequence name = FingerprintUtils.getInstance() + .getUniqueName(mContext, currentUserId); + final Fingerprint fingerprint = new Fingerprint(name, groupId, fingerId, deviceId); + final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client; enrollClient.onEnrollResult(fingerprint, remaining); }); @@ -224,8 +252,9 @@ class Fingerprint21 implements IHwBinder.DeathRecipient { if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) { Slog.e(TAG, "Got ERROR_HW_UNAVAILABLE"); - mDaemon = null; - mCurrentUserId = UserHandle.USER_NULL; + if (mCallback != null) { + mCallback.onHardwareUnavailable(); + } } }); } @@ -262,20 +291,27 @@ class Fingerprint21 implements IHwBinder.DeathRecipient { enumerateConsumer.onEnumerationResult(fp, remaining); }); } - }; + } - Fingerprint21(@NonNull Context context, int sensorId, + Fingerprint21(@NonNull Context context, @NonNull BiometricScheduler scheduler, + @NonNull Handler handler, int sensorId, @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { + @NonNull HalResultController controller) { mContext = context; + mScheduler = scheduler; + mHandler = handler; mActivityTaskManager = ActivityTaskManager.getService(); - mHandler = new Handler(Looper.getMainLooper()); + mTaskStackListener = new BiometricTaskStackListener(); mAuthenticatorIds = Collections.synchronizedMap(new HashMap<>()); mLazyDaemon = Fingerprint21.this::getDaemon; mLockoutResetDispatcher = lockoutResetDispatcher; mLockoutTracker = new LockoutFrameworkImpl(context, mLockoutResetCallback); - mScheduler = new BiometricScheduler(TAG, gestureAvailabilityDispatcher); + mHalResultController = controller; + mHalResultController.setCallback(() -> { + mDaemon = null; + mCurrentUserId = UserHandle.USER_NULL; + }); try { ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG); @@ -300,7 +336,21 @@ class Fingerprint21 implements IHwBinder.DeathRecipient { final @FingerprintSensorProperties.SensorType int sensorType = isUdfps ? FingerprintSensorProperties.TYPE_UDFPS : FingerprintSensorProperties.TYPE_REAR; - mSensorProperties = new FingerprintSensorProperties(sensorId, sensorType); + // resetLockout is controlled by the framework, so hardwareAuthToken is not required + final boolean resetLockoutRequiresHardwareAuthToken = false; + mSensorProperties = new FingerprintSensorProperties(sensorId, sensorType, + resetLockoutRequiresHardwareAuthToken); + } + + static Fingerprint21 newInstance(@NonNull Context context, int sensorId, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { + final Handler handler = new Handler(Looper.getMainLooper()); + final BiometricScheduler scheduler = + new BiometricScheduler(TAG, gestureAvailabilityDispatcher); + final HalResultController controller = new HalResultController(context, handler, scheduler); + return new Fingerprint21(context, scheduler, handler, sensorId, lockoutResetDispatcher, + controller); } @Override @@ -355,7 +405,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient { // successfully set. long halId = 0; try { - halId = mDaemon.setNotify(mDaemonCallback); + halId = mDaemon.setNotify(mHalResultController); } catch (RemoteException e) { Slog.e(TAG, "Failed to set callback for fingerprint HAL", e); mDaemon = null; @@ -373,6 +423,9 @@ class Fingerprint21 implements IHwBinder.DeathRecipient { return mDaemon; } + @Nullable IUdfpsOverlayController getUdfpsOverlayController() { + return mUdfpsOverlayController; + } @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) { return mLockoutTracker.getLockoutModeForUser(userId); } @@ -409,14 +462,17 @@ class Fingerprint21 implements IHwBinder.DeathRecipient { new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorProperties.sensorId, mCurrentUserId, hasEnrolled, mAuthenticatorIds); - mScheduler.scheduleClientMonitor(client, (clientMonitor, success) -> { - if (success) { - mCurrentUserId = targetUserId; + mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() { + @Override + public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, boolean success) { + if (success) { + mCurrentUserId = targetUserId; + } } }); } - void scheduleResetLockout(int userId, byte[] hardwareAuthToken) { + void scheduleResetLockout(int userId) { // Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler // thread. mHandler.post(() -> { @@ -453,12 +509,16 @@ class Fingerprint21 implements IHwBinder.DeathRecipient { mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, FingerprintUtils.getInstance(), ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController); - mScheduler.scheduleClientMonitor(client, ((clientMonitor, success) -> { - if (success) { - // Update authenticatorIds - scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId()); + mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() { + @Override + public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, + boolean success) { + if (success) { + // Update authenticatorIds + scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId()); + } } - })); + }); }); } @@ -485,7 +545,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient { void scheduleAuthenticate(@NonNull IBinder token, long operationId, int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName, - @Nullable Surface surface, boolean restricted, int statsClient) { + boolean restricted, int statsClient, boolean isKeyguard) { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); @@ -493,8 +553,8 @@ class Fingerprint21 implements IHwBinder.DeathRecipient { final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient( mContext, mLazyDaemon, token, listener, userId, operationId, restricted, opPackageName, cookie, false /* requireConfirmation */, - mSensorProperties.sensorId, isStrongBiometric, surface, statsClient, - mTaskStackListener, mLockoutTracker, mUdfpsOverlayController); + mSensorProperties.sensorId, isStrongBiometric, statsClient, + mTaskStackListener, mLockoutTracker, mUdfpsOverlayController, isKeyguard); mScheduler.scheduleClientMonitor(client); }); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java new file mode 100644 index 000000000000..1a6ad46fce67 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java @@ -0,0 +1,557 @@ +/* + * Copyright (C) 2020 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 com.android.server.biometrics.sensors.fingerprint; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.trust.TrustManager; +import android.content.ContentResolver; +import android.content.Context; +import android.hardware.fingerprint.FingerprintManager; +import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; +import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; +import android.hardware.fingerprint.FingerprintSensorProperties; +import android.hardware.fingerprint.IUdfpsOverlayController; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.Slog; +import android.util.SparseBooleanArray; + +import com.android.server.biometrics.sensors.AuthenticationConsumer; +import com.android.server.biometrics.sensors.BiometricScheduler; +import com.android.server.biometrics.sensors.ClientMonitor; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.LockoutResetDispatcher; + +import java.util.ArrayList; +import java.util.Random; + +/** + * A mockable/testable provider of the {@link android.hardware.biometrics.fingerprint.V2_3} HIDL + * interface. This class is intended simulate UDFPS logic for devices that do not have an actual + * fingerprint@2.3 HAL (where UDFPS starts to become supported) + * + * UDFPS "accept" can only happen within a set amount of time after a sensor authentication. This is + * specified by {@link MockHalResultController#AUTH_VALIDITY_MS}. Touches after this duration will + * be treated as "reject". + * + * This class provides framework logic to emulate, for testing only, the UDFPS functionalies below: + * + * 1) IF either A) the caller is keyguard, and the device is not in a trusted state (authenticated + * via biometric sensor or unlocked with a trust agent {@see android.app.trust.TrustManager}, OR + * B) the caller is not keyguard, and regardless of trusted state, AND (following applies to both + * (A) and (B) above) {@link FingerprintManager#onFingerDown(int, int, float, float)} is + * received, this class will respond with {@link AuthenticationCallback#onAuthenticationFailed()} + * after a tunable flat_time + variance_time. + * + * In the case above (1), callers must not receive a successful authentication event here because + * the sensor has not actually been authenticated. + * + * 2) IF A) the caller is keyguard and the device is not in a trusted state, OR B) the caller is not + * keyguard and regardless of trusted state, AND (following applies to both (A) and (B)) the + * sensor is touched and the fingerprint is accepted by the HAL, and then + * {@link FingerprintManager#onFingerDown(int, int, float, float)} is received, this class will + * respond with {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} + * after a tunable flat_time + variance_time. Note that the authentication callback from the + * sensor is held until {@link FingerprintManager#onFingerDown(int, int, float, float)} is + * invoked. + * + * In the case above (2), callers can receive a successful authentication callback because the + * real sensor was authenticated. Note that even though the real sensor was touched, keyguard + * fingerprint authentication does not put keyguard into a trusted state because the + * authentication callback is held until onFingerDown was invoked. This allows callers such as + * keyguard to simulate a realistic path. + * + * 3) IF the caller is keyguard AND the device in a trusted state and then + * {@link FingerprintManager#onFingerDown(int, int, float, float)} is received, this class will + * respond with {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} + * after a tunable flat_time + variance time. + * + * In the case above (3), since the device is already unlockable via trust agent, it's fine to + * simulate the successful auth path. Non-keyguard clients will fall into either (1) or (2) + * above. + * + * This class currently does not simulate false rejection. Conversely, this class relies on the + * real hardware sensor so does not affect false acceptance. + */ +@SuppressWarnings("deprecation") +public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManager.TrustListener { + + private static final String TAG = "Fingerprint21UdfpsMock"; + + // Secure setting integer. If true, the system will load this class to enable udfps testing. + public static final String CONFIG_ENABLE_TEST_UDFPS = + "com.android.server.biometrics.sensors.fingerprint.test_udfps.enable"; + // Secure setting integer. A fixed duration intended to simulate something like the duration + // required for image capture. + private static final String CONFIG_AUTH_DELAY_PT1 = + "com.android.server.biometrics.sensors.fingerprint.test_udfps.auth_delay_pt1"; + // Secure setting integer. A fixed duration intended to simulate something like the duration + // required for template matching to complete. + private static final String CONFIG_AUTH_DELAY_PT2 = + "com.android.server.biometrics.sensors.fingerprint.test_udfps.auth_delay_pt2"; + // Secure setting integer. A random value between [-randomness, randomness] will be added to the + // capture delay above for each accept/reject. + private static final String CONFIG_AUTH_DELAY_RANDOMNESS = + "com.android.server.biometrics.sensors.fingerprint.test_udfps.auth_delay_randomness"; + + private static final int DEFAULT_AUTH_DELAY_PT1_MS = 300; + private static final int DEFAULT_AUTH_DELAY_PT2_MS = 400; + private static final int DEFAULT_AUTH_DELAY_RANDOMNESS_MS = 100; + + @NonNull private final TestableBiometricScheduler mScheduler; + @NonNull private final Handler mHandler; + @NonNull private final FingerprintSensorProperties mSensorProperties; + @NonNull private final MockHalResultController mMockHalResultController; + @NonNull private final TrustManager mTrustManager; + @NonNull private final SparseBooleanArray mUserHasTrust; + @NonNull private final Random mRandom; + @NonNull private final FakeRejectRunnable mFakeRejectRunnable; + @NonNull private final FakeAcceptRunnable mFakeAcceptRunnable; + @NonNull private final RestartAuthRunnable mRestartAuthRunnable; + + private static class TestableBiometricScheduler extends BiometricScheduler { + @NonNull private final TestableInternalCallback mInternalCallback; + @NonNull private Fingerprint21UdfpsMock mFingerprint21; + + TestableBiometricScheduler(@NonNull String tag, + @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { + super(tag, gestureAvailabilityDispatcher); + mInternalCallback = new TestableInternalCallback(); + } + + class TestableInternalCallback extends InternalCallback { + @Override + public void onClientStarted(ClientMonitor<?> clientMonitor) { + super.onClientStarted(clientMonitor); + Slog.d(TAG, "Client started: " + clientMonitor); + mFingerprint21.setDebugMessage("Started: " + clientMonitor); + } + + @Override + public void onClientFinished(ClientMonitor<?> clientMonitor, boolean success) { + super.onClientFinished(clientMonitor, success); + Slog.d(TAG, "Client finished: " + clientMonitor); + mFingerprint21.setDebugMessage("Finished: " + clientMonitor); + } + } + + void init(@NonNull Fingerprint21UdfpsMock fingerprint21) { + mFingerprint21 = fingerprint21; + } + + /** + * Expose the internal finish callback so it can be used for testing + */ + @Override + @NonNull protected InternalCallback getInternalCallback() { + return mInternalCallback; + } + } + + /** + * All of the mocking/testing should happen in here. This way we don't need to modify the + * {@link com.android.server.biometrics.sensors.ClientMonitor} implementations and can run the + * real path there. + */ + private static class MockHalResultController extends HalResultController { + + // Duration for which a sensor authentication can be treated as UDFPS success. + private static final int AUTH_VALIDITY_MS = 10 * 1000; // 10 seconds + + static class LastAuthArgs { + @NonNull final AuthenticationConsumer lastAuthenticatedClient; + final long deviceId; + final int fingerId; + final int groupId; + @Nullable final ArrayList<Byte> token; + + LastAuthArgs(@NonNull AuthenticationConsumer authenticationConsumer, long deviceId, + int fingerId, int groupId, @Nullable ArrayList<Byte> token) { + lastAuthenticatedClient = authenticationConsumer; + this.deviceId = deviceId; + this.fingerId = fingerId; + this.groupId = groupId; + if (token == null) { + this.token = null; + } else { + // Store a copy so the owner can be GC'd + this.token = new ArrayList<>(token); + } + } + } + + // Initialized after the constructor, but before it's ever used. + @NonNull private RestartAuthRunnable mRestartAuthRunnable; + @NonNull private Fingerprint21UdfpsMock mFingerprint21; + @Nullable private LastAuthArgs mLastAuthArgs; + + MockHalResultController(@NonNull Context context, @NonNull Handler handler, + @NonNull BiometricScheduler scheduler) { + super(context, handler, scheduler); + } + + void init(@NonNull RestartAuthRunnable restartAuthRunnable, + @NonNull Fingerprint21UdfpsMock fingerprint21) { + mRestartAuthRunnable = restartAuthRunnable; + mFingerprint21 = fingerprint21; + } + + @Nullable AuthenticationConsumer getLastAuthenticatedClient() { + return mLastAuthArgs != null ? mLastAuthArgs.lastAuthenticatedClient : null; + } + + /** + * Intercepts the HAL authentication and holds it until the UDFPS simulation decides + * that authentication finished. + */ + @Override + public void onAuthenticated(long deviceId, int fingerId, int groupId, + ArrayList<Byte> token) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof AuthenticationConsumer)) { + Slog.e(TAG, "Non authentication consumer: " + client); + return; + } + + final boolean accepted = fingerId != 0; + if (accepted) { + mFingerprint21.setDebugMessage("Finger accepted"); + } else { + mFingerprint21.setDebugMessage("Finger rejected"); + } + + final AuthenticationConsumer authenticationConsumer = + (AuthenticationConsumer) client; + mLastAuthArgs = new LastAuthArgs(authenticationConsumer, deviceId, fingerId, + groupId, token); + + // Remove any existing restart runnbables since auth just started, and put a new + // one on the queue. + mHandler.removeCallbacks(mRestartAuthRunnable); + mRestartAuthRunnable.setLastAuthReference(authenticationConsumer); + mHandler.postDelayed(mRestartAuthRunnable, AUTH_VALIDITY_MS); + }); + } + + /** + * Calls through to the real interface and notifies clients of accept/reject. + */ + void sendAuthenticated(long deviceId, int fingerId, int groupId, + ArrayList<Byte> token) { + Slog.d(TAG, "sendAuthenticated: " + (fingerId != 0)); + mFingerprint21.setDebugMessage("Udfps match: " + (fingerId != 0)); + super.onAuthenticated(deviceId, fingerId, groupId, token); + } + } + + static Fingerprint21UdfpsMock newInstance(@NonNull Context context, int sensorId, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { + Slog.d(TAG, "Creating Fingerprint23Mock!"); + + final Handler handler = new Handler(Looper.getMainLooper()); + final TestableBiometricScheduler scheduler = + new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher); + final MockHalResultController controller = + new MockHalResultController(context, handler, scheduler); + return new Fingerprint21UdfpsMock(context, scheduler, handler, sensorId, + lockoutResetDispatcher, controller); + } + + private static abstract class FakeFingerRunnable implements Runnable { + private long mFingerDownTime; + private int mCaptureDuration; + + /** + * @param fingerDownTime System time when onFingerDown occurred + * @param captureDuration Duration that the finger needs to be down for + */ + void setSimulationTime(long fingerDownTime, int captureDuration) { + mFingerDownTime = fingerDownTime; + mCaptureDuration = captureDuration; + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + boolean isImageCaptureComplete() { + return System.currentTimeMillis() - mFingerDownTime > mCaptureDuration; + } + } + + private final class FakeRejectRunnable extends FakeFingerRunnable { + @Override + public void run() { + mMockHalResultController.sendAuthenticated(0, 0, 0, null); + } + } + + private final class FakeAcceptRunnable extends FakeFingerRunnable { + @Override + public void run() { + if (mMockHalResultController.mLastAuthArgs == null) { + // This can happen if the user has trust agents enabled, which make lockscreen + // dismissable. Send a fake non-zero (accept) finger. + Slog.d(TAG, "Sending fake finger"); + mMockHalResultController.sendAuthenticated(1 /* deviceId */, + 1 /* fingerId */, 1 /* groupId */, null /* token */); + } else { + mMockHalResultController.sendAuthenticated( + mMockHalResultController.mLastAuthArgs.deviceId, + mMockHalResultController.mLastAuthArgs.fingerId, + mMockHalResultController.mLastAuthArgs.groupId, + mMockHalResultController.mLastAuthArgs.token); + } + } + } + + /** + * The fingerprint HAL allows multiple (5) fingerprint attempts per HIDL invocation of the + * authenticate method. However, valid fingerprint authentications are invalidated after + * {@link MockHalResultController#AUTH_VALIDITY_MS}, meaning UDFPS touches will be reported as + * rejects if touched after that duration. However, since a valid fingerprint was detected, the + * HAL and FingerprintService will not look for subsequent fingerprints. + * + * In order to keep the FingerprintManager API consistent (that multiple fingerprint attempts + * are allowed per auth lifecycle), we internally cancel and restart authentication so that the + * sensor is responsive again. + */ + private static final class RestartAuthRunnable implements Runnable { + @NonNull private final Fingerprint21UdfpsMock mFingerprint21; + @NonNull private final TestableBiometricScheduler mScheduler; + + // Store a reference to the auth consumer that should be invalidated. + private AuthenticationConsumer mLastAuthConsumer; + + RestartAuthRunnable(@NonNull Fingerprint21UdfpsMock fingerprint21, + @NonNull TestableBiometricScheduler scheduler) { + mFingerprint21 = fingerprint21; + mScheduler = scheduler; + } + + void setLastAuthReference(AuthenticationConsumer lastAuthConsumer) { + mLastAuthConsumer = lastAuthConsumer; + } + + @Override + public void run() { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + + // We don't care about FingerprintDetectClient, since accept/rejects are both OK. UDFPS + // rejects will just simulate the path where non-enrolled fingers are presented. + if (!(client instanceof FingerprintAuthenticationClient)) { + Slog.e(TAG, "Non-FingerprintAuthenticationClient client: " + client); + return; + } + + // Perhaps the runnable is stale, or the user stopped/started auth manually. Do not + // restart auth in this case. + if (client != mLastAuthConsumer) { + Slog.e(TAG, "Current client: " + client + + " does not match mLastAuthConsumer: " + mLastAuthConsumer); + return; + } + + Slog.d(TAG, "Restarting auth, current: " + client); + mFingerprint21.setDebugMessage("Auth timed out"); + + final FingerprintAuthenticationClient authClient = + (FingerprintAuthenticationClient) client; + // Store the authClient parameters so it can be rescheduled + final IBinder token = client.getToken(); + final long operationId = authClient.getOperationId(); + final int user = client.getTargetUserId(); + final int cookie = client.getCookie(); + final ClientMonitorCallbackConverter listener = client.getListener(); + final String opPackageName = client.getOwnerString(); + final boolean restricted = authClient.isRestricted(); + final int statsClient = client.getStatsClient(); + final boolean isKeyguard = authClient.isKeyguard(); + + // Don't actually send cancel() to the HAL, since successful auth already finishes + // HAL authenticate() lifecycle. Just + mScheduler.getInternalCallback().onClientFinished(client, true /* success */); + + // Schedule this only after we invoke onClientFinished for the previous client, so that + // internal preemption logic is not run. + mFingerprint21.scheduleAuthenticate(token, operationId, user, cookie, + listener, opPackageName, restricted, statsClient, isKeyguard); + } + } + + private Fingerprint21UdfpsMock(@NonNull Context context, + @NonNull TestableBiometricScheduler scheduler, + @NonNull Handler handler, int sensorId, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull MockHalResultController controller) { + super(context, scheduler, handler, sensorId, lockoutResetDispatcher, controller); + mScheduler = scheduler; + mScheduler.init(this); + mHandler = handler; + // resetLockout is controlled by the framework, so hardwareAuthToken is not required + final boolean resetLockoutRequiresHardwareAuthToken = false; + mSensorProperties = new FingerprintSensorProperties(sensorId, + FingerprintSensorProperties.TYPE_UDFPS, resetLockoutRequiresHardwareAuthToken); + mMockHalResultController = controller; + mUserHasTrust = new SparseBooleanArray(); + mTrustManager = context.getSystemService(TrustManager.class); + mTrustManager.registerTrustListener(this); + mRandom = new Random(); + mFakeRejectRunnable = new FakeRejectRunnable(); + mFakeAcceptRunnable = new FakeAcceptRunnable(); + mRestartAuthRunnable = new RestartAuthRunnable(this, mScheduler); + + // We can't initialize this during MockHalresultController's constructor due to a circular + // dependency. + mMockHalResultController.init(mRestartAuthRunnable, this); + } + + @Override + public void onTrustChanged(boolean enabled, int userId, int flags) { + mUserHasTrust.put(userId, enabled); + } + + @Override + public void onTrustManagedChanged(boolean enabled, int userId) { + + } + + @Override + public void onTrustError(CharSequence message) { + + } + + @Override + @NonNull + FingerprintSensorProperties getFingerprintSensorProperties() { + return mSensorProperties; + } + + @Override + void onFingerDown(int x, int y, float minor, float major) { + mHandler.post(() -> { + Slog.d(TAG, "onFingerDown"); + final AuthenticationConsumer lastAuthenticatedConsumer = + mMockHalResultController.getLastAuthenticatedClient(); + final ClientMonitor<?> currentScheduledClient = mScheduler.getCurrentClient(); + + if (currentScheduledClient == null) { + Slog.d(TAG, "Not authenticating"); + return; + } + + mHandler.removeCallbacks(mFakeRejectRunnable); + mHandler.removeCallbacks(mFakeAcceptRunnable); + + // The sensor was authenticated, is still the currently scheduled client, and the + // user touched the UDFPS affordance. Pretend that auth succeeded. + final boolean authenticatedClientIsCurrent = lastAuthenticatedConsumer != null + && lastAuthenticatedConsumer == currentScheduledClient; + // User is unlocked on keyguard via Trust Agent + final boolean keyguardAndTrusted; + if (currentScheduledClient instanceof FingerprintAuthenticationClient) { + keyguardAndTrusted = ((FingerprintAuthenticationClient) currentScheduledClient) + .isKeyguard() + && mUserHasTrust.get(currentScheduledClient.getTargetUserId(), false); + } else { + keyguardAndTrusted = false; + } + + final int captureDuration = getNewCaptureDuration(); + final int matchingDuration = getMatchingDuration(); + final int totalDuration = captureDuration + matchingDuration; + setDebugMessage("Duration: " + totalDuration + + " (" + captureDuration + " + " + matchingDuration + ")"); + if (authenticatedClientIsCurrent || keyguardAndTrusted) { + mFakeAcceptRunnable.setSimulationTime(System.currentTimeMillis(), captureDuration); + mHandler.postDelayed(mFakeAcceptRunnable, totalDuration); + } else if (currentScheduledClient instanceof AuthenticationConsumer) { + // Something is authenticating but authentication has not succeeded yet. Pretend + // that auth rejected. + mFakeRejectRunnable.setSimulationTime(System.currentTimeMillis(), captureDuration); + mHandler.postDelayed(mFakeRejectRunnable, totalDuration); + } + }); + } + + @Override + void onFingerUp() { + mHandler.post(() -> { + Slog.d(TAG, "onFingerUp"); + + // Only one of these can be on the handler at any given time (see onFingerDown). If + // image capture is not complete, send ACQUIRED_TOO_FAST and remove the runnable from + // the handler. Image capture (onFingerDown) needs to happen again. + if (mHandler.hasCallbacks(mFakeRejectRunnable) + && !mFakeRejectRunnable.isImageCaptureComplete()) { + mHandler.removeCallbacks(mFakeRejectRunnable); + mMockHalResultController.onAcquired(0 /* deviceId */, + FingerprintManager.FINGERPRINT_ACQUIRED_TOO_FAST, + 0 /* vendorCode */); + } else if (mHandler.hasCallbacks(mFakeAcceptRunnable) + && !mFakeAcceptRunnable.isImageCaptureComplete()) { + mHandler.removeCallbacks(mFakeAcceptRunnable); + mMockHalResultController.onAcquired(0 /* deviceId */, + FingerprintManager.FINGERPRINT_ACQUIRED_TOO_FAST, + 0 /* vendorCode */); + } + }); + } + + private int getNewCaptureDuration() { + final ContentResolver contentResolver = mContext.getContentResolver(); + final int captureTime = Settings.Secure.getIntForUser(contentResolver, + CONFIG_AUTH_DELAY_PT1, + DEFAULT_AUTH_DELAY_PT1_MS, + UserHandle.USER_CURRENT); + final int randomDelayRange = Settings.Secure.getIntForUser(contentResolver, + CONFIG_AUTH_DELAY_RANDOMNESS, + DEFAULT_AUTH_DELAY_RANDOMNESS_MS, + UserHandle.USER_CURRENT); + final int randomDelay = mRandom.nextInt(randomDelayRange * 2) - randomDelayRange; + + // Must be at least 0 + return Math.max(captureTime + randomDelay, 0); + } + + private int getMatchingDuration() { + final int matchingTime = Settings.Secure.getIntForUser(mContext.getContentResolver(), + CONFIG_AUTH_DELAY_PT2, + DEFAULT_AUTH_DELAY_PT2_MS, + UserHandle.USER_CURRENT); + + // Must be at least 0 + return Math.max(matchingTime, 0); + } + + private void setDebugMessage(String message) { + try { + final IUdfpsOverlayController controller = getUdfpsOverlayController(); + // Things can happen before SysUI loads and sets the controller. + if (controller != null) { + Slog.d(TAG, "setDebugMessage: " + message); + controller.setDebugMessage(message); + } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when sending message: " + message, e); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java index 1564056cfdbd..99d348a780f3 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java @@ -29,7 +29,6 @@ import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; -import android.view.Surface; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; @@ -47,6 +46,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi private static final String TAG = "Biometrics/FingerprintAuthClient"; + private final boolean mIsKeyguard; private final LockoutFrameworkImpl mLockoutFrameworkImpl; @Nullable private final IUdfpsOverlayController mUdfpsOverlayController; @@ -54,16 +54,17 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation, - int sensorId, boolean isStrongBiometric, @Nullable Surface surface, int statsClient, + int sensorId, boolean isStrongBiometric, int statsClient, @NonNull TaskStackListener taskStackListener, @NonNull LockoutFrameworkImpl lockoutTracker, - @Nullable IUdfpsOverlayController udfpsOverlayController) { + @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isKeyguard) { super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner, cookie, requireConfirmation, sensorId, isStrongBiometric, BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener, lockoutTracker); mLockoutFrameworkImpl = lockoutTracker; mUdfpsOverlayController = udfpsOverlayController; + mIsKeyguard = isKeyguard; } @Override @@ -79,7 +80,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi if (authenticated) { resetFailedAttempts(getTargetUserId()); UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController); - mFinishCallback.onClientFinished(this, true /* success */); + mCallback.onClientFinished(this, true /* success */); } else { final @LockoutTracker.LockoutMode int lockoutMode = mLockoutFrameworkImpl.getLockoutModeForUser(getTargetUserId()); @@ -119,7 +120,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } @@ -132,7 +133,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi Slog.e(TAG, "Remote exception when requesting cancel", e); onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } @@ -145,4 +146,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi public void onFingerUp() { UdfpsHelper.onFingerUp(getFreshDaemon()); } + + public boolean isKeyguard() { + return mIsKeyguard; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java index 3418c466aa69..21a46d58a3b3 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java @@ -75,11 +75,6 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub } @Override - public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException { - mFingerprintService.resetLockout(userId, hardwareAuthToken); - } - - @Override public long getAuthenticatorId(int callingUserId) throws RemoteException { return mFingerprintService.getAuthenticatorId(callingUserId); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintDetectClient.java index 8b295f8f4931..8652ee403089 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintDetectClient.java @@ -68,13 +68,13 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint> Slog.e(TAG, "Remote exception when requesting cancel", e); onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } @Override - public void start(@NonNull FinishCallback finishCallback) { - super.start(finishCallback); + public void start(@NonNull Callback callback) { + super.start(callback); startHalOperation(); } @@ -88,7 +88,7 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint> onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java index 32f8b8fe8b26..d5db6e411b95 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java @@ -79,7 +79,7 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } @@ -92,7 +92,7 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint Slog.e(TAG, "Remote exception when requesting cancel", e); onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java index 240c3c56f75e..834bf42eba3f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java @@ -52,7 +52,7 @@ class FingerprintInternalEnumerateClient extends InternalEnumerateClient<IBiomet getFreshDaemon().enumerate(); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting enumerate", e); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java index a9336ef6a6c2..9f5456300884 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java @@ -54,7 +54,7 @@ class FingerprintRemovalClient extends RemovalClient<IBiometricsFingerprint> { getFreshDaemon().remove(getTargetUserId(), mBiometricId); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting remove", e); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index e4387c9e2b81..7c7da118df10 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -25,6 +25,7 @@ import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.Manifest.permission.USE_FINGERPRINT; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; @@ -38,14 +39,17 @@ import android.hardware.fingerprint.IFingerprintService; import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.NativeHandle; import android.os.Process; import android.os.UserHandle; +import android.provider.Settings; import android.util.EventLog; import android.util.Slog; import android.view.Surface; +import com.android.internal.R; import com.android.internal.util.DumpUtils; import com.android.internal.widget.LockPatternUtils; import com.android.server.SystemService; @@ -94,10 +98,16 @@ public class FingerprintService extends SystemService { } @Override // Binder call - public void generateChallenge(IBinder token, IFingerprintServiceReceiver receiver, - String opPackageName) { + public void generateChallenge(IBinder token, int sensorId, + IFingerprintServiceReceiver receiver, String opPackageName) { Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); - mFingerprint21.scheduleGenerateChallenge(token, receiver, opPackageName); + + if (sensorId == mFingerprint21.getFingerprintSensorProperties().sensorId) { + mFingerprint21.scheduleGenerateChallenge(token, receiver, opPackageName); + return; + } + + Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId); } @Override // Binder call @@ -158,8 +168,8 @@ public class FingerprintService extends SystemService { final int statsClient = isKeyguard ? BiometricsProtoEnums.CLIENT_KEYGUARD : BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER; mFingerprint21.scheduleAuthenticate(token, operationId, userId, 0 /* cookie */, - new ClientMonitorCallbackConverter(receiver), opPackageName, surface, - restricted, statsClient); + new ClientMonitorCallbackConverter(receiver), opPackageName, + restricted, statsClient, isKeyguard); } @Override @@ -193,8 +203,8 @@ public class FingerprintService extends SystemService { final boolean restricted = true; // BiometricPrompt is always restricted mFingerprint21.scheduleAuthenticate(token, operationId, userId, cookie, - new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, surface, - restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT); + new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, restricted, + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, false /* isKeyguard */); } @Override // Binder call @@ -343,9 +353,16 @@ public class FingerprintService extends SystemService { } @Override // Binder call - public void resetLockout(int userId, byte [] hardwareAuthToken) { + public void resetLockout(IBinder token, int sensorId, int userId, + @Nullable byte [] hardwareAuthToken, String opPackageName) { Utils.checkPermission(getContext(), RESET_FINGERPRINT_LOCKOUT); - mFingerprint21.scheduleResetLockout(userId, hardwareAuthToken); + + if (sensorId == mFingerprint21.getFingerprintSensorProperties().sensorId) { + mFingerprint21.scheduleResetLockout(userId); + return; + } + + Slog.w(TAG, "No matching sensor for resetLockout, sensorId: " + sensorId); } @Override @@ -369,8 +386,18 @@ public class FingerprintService extends SystemService { @Override // Binder call public void initializeConfiguration(int sensorId) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); - mFingerprint21 = new Fingerprint21(getContext(), sensorId, mLockoutResetDispatcher, - mGestureAvailabilityDispatcher); + + if ((Build.IS_USERDEBUG || Build.IS_ENG) + && getContext().getResources().getBoolean(R.bool.allow_test_udfps) + && Settings.Secure.getIntForUser(getContext().getContentResolver(), + Fingerprint21UdfpsMock.CONFIG_ENABLE_TEST_UDFPS, 0 /* default */, + UserHandle.USER_CURRENT) != 0) { + mFingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(), sensorId, + mLockoutResetDispatcher, mGestureAvailabilityDispatcher); + } else { + mFingerprint21 = Fingerprint21.newInstance(getContext(), sensorId, + mLockoutResetDispatcher, mGestureAvailabilityDispatcher); + } } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java index e1082ae51575..c1c3593db564 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java @@ -22,7 +22,6 @@ import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.os.Build; import android.os.Environment; -import android.os.IBinder; import android.os.RemoteException; import android.os.SELinux; import android.util.Slog; @@ -58,8 +57,8 @@ public class FingerprintUpdateActiveUserClient extends ClientMonitor<IBiometrics } @Override - public void start(@NonNull FinishCallback finishCallback) { - super.start(finishCallback); + public void start(@NonNull Callback callback) { + super.start(callback); if (mCurrentUserId == getTargetUserId()) { Slog.d(TAG, "Already user: " + mCurrentUserId + ", refreshing authenticatorId"); @@ -69,7 +68,7 @@ public class FingerprintUpdateActiveUserClient extends ClientMonitor<IBiometrics } catch (RemoteException e) { Slog.e(TAG, "Unable to refresh authenticatorId", e); } - finishCallback.onClientFinished(this, true /* success */); + callback.onClientFinished(this, true /* success */); return; } @@ -89,7 +88,7 @@ public class FingerprintUpdateActiveUserClient extends ClientMonitor<IBiometrics if (!mDirectory.exists()) { if (!mDirectory.mkdir()) { Slog.e(TAG, "Cannot make directory: " + mDirectory.getAbsolutePath()); - finishCallback.onClientFinished(this, false /* success */); + callback.onClientFinished(this, false /* success */); return; } // Calling mkdir() from this process will create a directory with our @@ -97,7 +96,7 @@ public class FingerprintUpdateActiveUserClient extends ClientMonitor<IBiometrics // the label. if (!SELinux.restorecon(mDirectory)) { Slog.e(TAG, "Restorecons failed. Directory will have wrong label."); - finishCallback.onClientFinished(this, false /* success */); + callback.onClientFinished(this, false /* success */); return; } } @@ -116,10 +115,10 @@ public class FingerprintUpdateActiveUserClient extends ClientMonitor<IBiometrics getFreshDaemon().setActiveGroup(getTargetUserId(), mDirectory.getAbsolutePath()); mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics ? getFreshDaemon().getAuthenticatorId() : 0L); - mFinishCallback.onClientFinished(this, true /* success */); + mCallback.onClientFinished(this, true /* success */); } catch (RemoteException e) { Slog.e(TAG, "Failed to setActiveGroup: " + e); - mFinishCallback.onClientFinished(this, false /* success */); + mCallback.onClientFinished(this, false /* success */); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java index 9e0405792746..0400ef522142 100644 --- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java @@ -70,10 +70,6 @@ public final class IrisAuthenticator extends IBiometricAuthenticator.Stub { } @Override - public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException { - } - - @Override public long getAuthenticatorId(int callingUserId) throws RemoteException { return 0; } diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index 7202f0f401f9..7f9b3c9fcff7 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -56,7 +56,6 @@ import android.system.OsConstants; import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; -import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -130,7 +129,42 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse } } - public PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd) { + /** + * A data class to store each uid Netd permission information. Netd permissions includes + * PERMISSION_NETWORK, PERMISSION_SYSTEM, PERMISSION_INTERNET, PERMISSION_UPDATE_DEVICE_STATS + * and OR'd with the others. Default permission is PERMISSION_NONE and PERMISSION_UNINSTALLED + * will be set if all packages are removed from the uid. + */ + public static class UidNetdPermissionInfo { + private final int mNetdPermissions; + + UidNetdPermissionInfo() { + this(PERMISSION_NONE); + } + + UidNetdPermissionInfo(int permissions) { + mNetdPermissions = permissions; + } + + /** Plus given permissions and return new UidNetdPermissionInfo instance. */ + public UidNetdPermissionInfo plusNetdPermissions(int permissions) { + return new UidNetdPermissionInfo(mNetdPermissions | permissions); + } + + /** Return whether package is uninstalled. */ + public boolean isPackageUninstalled() { + return mNetdPermissions == PERMISSION_UNINSTALLED; + } + + /** Check that uid has given permissions */ + public boolean hasNetdPermissions(final int permissions) { + if (isPackageUninstalled()) return false; + if (permissions == PERMISSION_NONE) return true; + return (mNetdPermissions & permissions) == permissions; + } + } + + public PermissionMonitor(Context context, INetd netd) { this(context, netd, new Dependencies()); } @@ -161,7 +195,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse return; } - SparseIntArray netdPermsUids = new SparseIntArray(); + final SparseArray<UidNetdPermissionInfo> netdPermsUids = new SparseArray<>(); for (PackageInfo app : apps) { int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID; @@ -183,9 +217,13 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse } } + // Skip already checked uid. + if (netdPermsUids.get(uid) != null) continue; + //TODO: unify the management of the permissions into one codepath. - final int otherNetdPerms = getNetdPermissionMask(uid); - netdPermsUids.put(uid, netdPermsUids.get(uid) | otherNetdPerms); + final UidNetdPermissionInfo permInfo = + new UidNetdPermissionInfo(getNetdPermissionMask(uid)); + netdPermsUids.put(uid, permInfo); } List<UserInfo> users = mUserManager.getUsers(true); // exclude dying users @@ -207,7 +245,10 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse ? PERMISSION_UPDATE_DEVICE_STATS : 0; netdPermission |= perms.contains(INTERNET) ? PERMISSION_INTERNET : 0; } - netdPermsUids.put(uid, netdPermsUids.get(uid) | netdPermission); + final UidNetdPermissionInfo permInfo = netdPermsUids.get(uid); + netdPermsUids.put(uid, permInfo != null + ? permInfo.plusNetdPermissions(netdPermission) + : new UidNetdPermissionInfo(netdPermission)); } log("Users: " + mUsers.size() + ", Apps: " + mApps.size()); update(mUsers, mApps, true); @@ -341,15 +382,15 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse return currentPermission; } - private int getPermissionForUid(final int uid) { + private UidNetdPermissionInfo getPermissionForUid(final int uid) { // Check all the packages for this UID. The UID has the permission if any of the // packages in it has the permission. final String[] packages = mPackageManager.getPackagesForUid(uid); if (packages == null || packages.length <= 0) { // The last package of this uid is removed from device. Clean the package up. - return PERMISSION_UNINSTALLED; + return new UidNetdPermissionInfo(PERMISSION_UNINSTALLED); } - return getNetdPermissionMask(uid); + return new UidNetdPermissionInfo(getNetdPermissionMask(uid)); } /** @@ -599,28 +640,28 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse * permission information to netd. * * @param uid the app uid of the package installed - * @param permissions the permissions the app requested and netd cares about. + * @param permissionInfo the permission info of given uid. * * @hide */ @VisibleForTesting - void sendPackagePermissionsForUid(int uid, int permissions) { - SparseIntArray netdPermissionsAppIds = new SparseIntArray(); - netdPermissionsAppIds.put(uid, permissions); - sendPackagePermissionsToNetd(netdPermissionsAppIds); + void sendPackagePermissionsForUid(int uid, UidNetdPermissionInfo permissionInfo) { + final SparseArray<UidNetdPermissionInfo> uidsPermInfo = new SparseArray<>(); + uidsPermInfo.put(uid, permissionInfo); + sendPackagePermissionsToNetd(uidsPermInfo); } /** * Called by packageManagerService to send IPC to netd. Grant or revoke the INTERNET * and/or UPDATE_DEVICE_STATS permission of the uids in array. * - * @param netdPermissionsAppIds integer pairs of uids and the permission granted to it. If the - * permission is 0, revoke all permissions of that uid. - * + * @param uidsPermInfo permission info array generated from each uid. If the uid permission is + * PERMISSION_NONE or PERMISSION_UNINSTALLED, revoke all permissions of that + * uid. * @hide */ @VisibleForTesting - void sendPackagePermissionsToNetd(SparseIntArray netdPermissionsAppIds) { + void sendPackagePermissionsToNetd(final SparseArray<UidNetdPermissionInfo> uidsPermInfo) { if (mNetd == null) { Log.e(TAG, "Failed to get the netd service"); return; @@ -630,26 +671,20 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>(); ArrayList<Integer> noPermissionAppIds = new ArrayList<>(); ArrayList<Integer> uninstalledAppIds = new ArrayList<>(); - for (int i = 0; i < netdPermissionsAppIds.size(); i++) { - int permissions = netdPermissionsAppIds.valueAt(i); - switch(permissions) { - case (PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS): - allPermissionAppIds.add(netdPermissionsAppIds.keyAt(i)); - break; - case PERMISSION_INTERNET: - internetPermissionAppIds.add(netdPermissionsAppIds.keyAt(i)); - break; - case PERMISSION_UPDATE_DEVICE_STATS: - updateStatsPermissionAppIds.add(netdPermissionsAppIds.keyAt(i)); - break; - case PERMISSION_NONE: - noPermissionAppIds.add(netdPermissionsAppIds.keyAt(i)); - break; - case PERMISSION_UNINSTALLED: - uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i)); - default: - Log.e(TAG, "unknown permission type: " + permissions + "for uid: " - + netdPermissionsAppIds.keyAt(i)); + for (int i = 0; i < uidsPermInfo.size(); i++) { + final int uid = uidsPermInfo.keyAt(i); + final UidNetdPermissionInfo permInfo = uidsPermInfo.valueAt(i); + if (permInfo.hasNetdPermissions( + PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS)) { + allPermissionAppIds.add(uid); + } else if (permInfo.hasNetdPermissions(PERMISSION_INTERNET)) { + internetPermissionAppIds.add(uid); + } else if (permInfo.hasNetdPermissions(PERMISSION_UPDATE_DEVICE_STATS)) { + updateStatsPermissionAppIds.add(uid); + } else if (permInfo.isPackageUninstalled()) { + uninstalledAppIds.add(uid); + } else { + noPermissionAppIds.add(uid); } } try { diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 1c93d4eb599b..5484bfca5851 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -153,8 +153,8 @@ public class Vpn { private static final boolean LOGD = true; // Length of time (in milliseconds) that an app hosting an always-on VPN is placed on - // the device idle whitelist during service launch and VPN bootstrap. - private static final long VPN_LAUNCH_IDLE_WHITELIST_DURATION_MS = 60 * 1000; + // the device idle allowlist during service launch and VPN bootstrap. + private static final long VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS = 60 * 1000; // Settings for how much of the address space should be routed so that Vpn considers // "most" of the address space is routed. This is used to determine whether this Vpn @@ -180,7 +180,8 @@ public class Vpn { // This is taken as a total of IPv4 + IPV6 routes for simplicity, but the algorithm // is actually O(n²)+O(n²). private static final int MAX_ROUTES_TO_EVALUATE = 150; - + private static final String LOCKDOWN_ALLOWLIST_SETTING_NAME = + Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST; /** * Largest profile size allowable for Platform VPNs. * @@ -236,7 +237,7 @@ public class Vpn { * Set of packages in addition to the VPN app itself that can access the network directly when * VPN is not connected even if {@code mLockdown} is set. */ - private @NonNull List<String> mLockdownWhitelist = Collections.emptyList(); + private @NonNull List<String> mLockdownAllowlist = Collections.emptyList(); /** * A memory of what UIDs this class told netd to block for the lockdown feature. @@ -520,7 +521,7 @@ public class Vpn { } } if (!hadUnderlyingNetworks) { - // No idea what the underlying networks are; assume sane defaults + // No idea what the underlying networks are; assume the safer defaults metered = true; roaming = false; congested = false; @@ -653,18 +654,18 @@ public class Vpn { * * @param packageName the package to designate as always-on VPN supplier. * @param lockdown whether to prevent traffic outside of a VPN, for example while connecting. - * @param lockdownWhitelist packages to be whitelisted from lockdown. + * @param lockdownAllowlist packages to be allowed from lockdown. * @param keyStore the Keystore instance to use for checking of PlatformVpnProfile(s) * @return {@code true} if the package has been set as always-on, {@code false} otherwise. */ public synchronized boolean setAlwaysOnPackage( @Nullable String packageName, boolean lockdown, - @Nullable List<String> lockdownWhitelist, + @Nullable List<String> lockdownAllowlist, @NonNull KeyStore keyStore) { enforceControlPermissionOrInternalCaller(); - if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownWhitelist, keyStore)) { + if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownAllowlist, keyStore)) { saveAlwaysOnPackage(); return true; } @@ -679,7 +680,7 @@ public class Vpn { * * @param packageName the package to designate as always-on VPN supplier. * @param lockdown whether to prevent traffic outside of a VPN, for example while connecting. - * @param lockdownWhitelist packages to be whitelisted from lockdown. This is only used if + * @param lockdownAllowlist packages to be allowed to bypass lockdown. This is only used if * {@code lockdown} is {@code true}. Packages must not contain commas. * @param keyStore the system keystore instance to check for profiles * @return {@code true} if the package has been set as always-on, {@code false} otherwise. @@ -687,16 +688,16 @@ public class Vpn { @GuardedBy("this") private boolean setAlwaysOnPackageInternal( @Nullable String packageName, boolean lockdown, - @Nullable List<String> lockdownWhitelist, @NonNull KeyStore keyStore) { + @Nullable List<String> lockdownAllowlist, @NonNull KeyStore keyStore) { if (VpnConfig.LEGACY_VPN.equals(packageName)) { Log.w(TAG, "Not setting legacy VPN \"" + packageName + "\" as always-on."); return false; } - if (lockdownWhitelist != null) { - for (String pkg : lockdownWhitelist) { + if (lockdownAllowlist != null) { + for (String pkg : lockdownAllowlist) { if (pkg.contains(",")) { - Log.w(TAG, "Not setting always-on vpn, invalid whitelisted package: " + pkg); + Log.w(TAG, "Not setting always-on vpn, invalid allowed package: " + pkg); return false; } } @@ -724,8 +725,8 @@ public class Vpn { } mLockdown = (mAlwaysOn && lockdown); - mLockdownWhitelist = (mLockdown && lockdownWhitelist != null) - ? Collections.unmodifiableList(new ArrayList<>(lockdownWhitelist)) + mLockdownAllowlist = (mLockdown && lockdownAllowlist != null) + ? Collections.unmodifiableList(new ArrayList<>(lockdownAllowlist)) : Collections.emptyList(); if (isCurrentPreparedPackage(packageName)) { @@ -754,10 +755,10 @@ public class Vpn { } /** - * @return an immutable list of packages whitelisted from always-on VPN lockdown. + * @return an immutable list of packages allowed to bypass always-on VPN lockdown. */ - public synchronized List<String> getLockdownWhitelist() { - return mLockdown ? mLockdownWhitelist : null; + public synchronized List<String> getLockdownAllowlist() { + return mLockdown ? mLockdownAllowlist : null; } /** @@ -772,8 +773,8 @@ public class Vpn { mSystemServices.settingsSecurePutIntForUser(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN, (mAlwaysOn && mLockdown ? 1 : 0), mUserHandle); mSystemServices.settingsSecurePutStringForUser( - Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST, - String.join(",", mLockdownWhitelist), mUserHandle); + LOCKDOWN_ALLOWLIST_SETTING_NAME, + String.join(",", mLockdownAllowlist), mUserHandle); } finally { Binder.restoreCallingIdentity(token); } @@ -788,12 +789,12 @@ public class Vpn { Settings.Secure.ALWAYS_ON_VPN_APP, mUserHandle); final boolean alwaysOnLockdown = mSystemServices.settingsSecureGetIntForUser( Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN, 0 /*default*/, mUserHandle) != 0; - final String whitelistString = mSystemServices.settingsSecureGetStringForUser( - Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST, mUserHandle); - final List<String> whitelistedPackages = TextUtils.isEmpty(whitelistString) - ? Collections.emptyList() : Arrays.asList(whitelistString.split(",")); + final String allowlistString = mSystemServices.settingsSecureGetStringForUser( + LOCKDOWN_ALLOWLIST_SETTING_NAME, mUserHandle); + final List<String> allowedPackages = TextUtils.isEmpty(allowlistString) + ? Collections.emptyList() : Arrays.asList(allowlistString.split(",")); setAlwaysOnPackageInternal( - alwaysOnPackage, alwaysOnLockdown, whitelistedPackages, keyStore); + alwaysOnPackage, alwaysOnLockdown, allowedPackages, keyStore); } finally { Binder.restoreCallingIdentity(token); } @@ -849,7 +850,7 @@ public class Vpn { DeviceIdleInternal idleController = LocalServices.getService(DeviceIdleInternal.class); idleController.addPowerSaveTempWhitelistApp(Process.myUid(), alwaysOnPackage, - VPN_LAUNCH_IDLE_WHITELIST_DURATION_MS, mUserHandle, false, "vpn"); + VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS, mUserHandle, false, "vpn"); // Start the VPN service declared in the app's manifest. Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE); @@ -1212,7 +1213,7 @@ public class Vpn { // applications have changed. Consider diffing UID ranges and only applying the delta. if (!Objects.equals(oldConfig.allowedApplications, mConfig.allowedApplications) || !Objects.equals(oldConfig.disallowedApplications, mConfig.disallowedApplications)) { - Log.i(TAG, "Handover not possible due to changes to whitelisted/blacklisted apps"); + Log.i(TAG, "Handover not possible due to changes to allowed/denied apps"); return false; } @@ -1440,13 +1441,13 @@ public class Vpn { * associated with one user, and any restricted profiles attached to that user. * * <p>If one of {@param allowedApplications} or {@param disallowedApplications} is provided, - * the UID ranges will match the app whitelist or blacklist specified there. Otherwise, all UIDs + * the UID ranges will match the app list specified there. Otherwise, all UIDs * in each user and profile will be included. * * @param userHandle The userId to create UID ranges for along with any of its restricted * profiles. - * @param allowedApplications (optional) whitelist of applications to include. - * @param disallowedApplications (optional) blacklist of applications to exclude. + * @param allowedApplications (optional) List of applications to allow. + * @param disallowedApplications (optional) List of applications to deny. */ @VisibleForTesting Set<UidRange> createUserAndRestrictedProfilesRanges(@UserIdInt int userHandle, @@ -1480,13 +1481,13 @@ public class Vpn { * associated with one user. * * <p>If one of {@param allowedApplications} or {@param disallowedApplications} is provided, - * the UID ranges will match the app whitelist or blacklist specified there. Otherwise, all UIDs + * the UID ranges will match the app allowlist or denylist specified there. Otherwise, all UIDs * in the user will be included. * * @param ranges {@link Set} of {@link UidRange}s to which to add. * @param userHandle The userId to add to {@param ranges}. - * @param allowedApplications (optional) whitelist of applications to include. - * @param disallowedApplications (optional) blacklist of applications to exclude. + * @param allowedApplications (optional) allowlist of applications to include. + * @param disallowedApplications (optional) denylist of applications to exclude. */ @VisibleForTesting void addUserToRanges(@NonNull Set<UidRange> ranges, @UserIdInt int userHandle, @@ -1608,7 +1609,7 @@ public class Vpn { /** * Restricts network access from all UIDs affected by this {@link Vpn}, apart from the VPN - * service app itself and whitelisted packages, to only sockets that have had {@code protect()} + * service app itself and allowed packages, to only sockets that have had {@code protect()} * called on them. All non-VPN traffic is blocked via a {@code PROHIBIT} response from the * kernel. * @@ -1630,7 +1631,7 @@ public class Vpn { if (isNullOrLegacyVpn(mPackage)) { exemptedPackages = null; } else { - exemptedPackages = new ArrayList<>(mLockdownWhitelist); + exemptedPackages = new ArrayList<>(mLockdownAllowlist); exemptedPackages.add(mPackage); } final Set<UidRange> rangesToTellNetdToRemove = new ArraySet<>(mBlockedUidsAsToldToNetd); @@ -1675,7 +1676,7 @@ public class Vpn { * Tell netd to add or remove a list of {@link UidRange}s to the list of UIDs that are only * allowed to make connections through sockets that have had {@code protect()} called on them. * - * @param enforce {@code true} to add to the blacklist, {@code false} to remove. + * @param enforce {@code true} to add to the denylist, {@code false} to remove. * @param ranges {@link Collection} of {@link UidRange}s to add (if {@param enforce} is * {@code true}) or to remove. * @return {@code true} if all of the UIDs were added/removed. {@code false} otherwise, diff --git a/services/core/java/com/android/server/gpu/GpuService.java b/services/core/java/com/android/server/gpu/GpuService.java index c0617ca95f9f..54794fecbf5b 100644 --- a/services/core/java/com/android/server/gpu/GpuService.java +++ b/services/core/java/com/android/server/gpu/GpuService.java @@ -65,7 +65,7 @@ public class GpuService extends SystemService { private static final String PROD_DRIVER_PROPERTY = "ro.gfx.driver.0"; private static final String DEV_DRIVER_PROPERTY = "ro.gfx.driver.1"; - private static final String GAME_DRIVER_ALLOWLIST_FILENAME = "allowlist.txt"; + private static final String UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST_FILENAME = "allowlist.txt"; private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP; private final Context mContext; @@ -77,7 +77,7 @@ public class GpuService extends SystemService { private final boolean mHasProdDriver; private final boolean mHasDevDriver; private ContentResolver mContentResolver; - private long mGameDriverVersionCode; + private long mProdDriverVersionCode; private SettingsObserver mSettingsObserver; private DeviceConfigListener mDeviceConfigListener; @GuardedBy("mLock") @@ -88,7 +88,7 @@ public class GpuService extends SystemService { mContext = context; mProdDriverPackageName = SystemProperties.get(PROD_DRIVER_PROPERTY); - mGameDriverVersionCode = -1; + mProdDriverVersionCode = -1; mDevDriverPackageName = SystemProperties.get(DEV_DRIVER_PROPERTY); mPackageManager = context.getPackageManager(); mHasProdDriver = !TextUtils.isEmpty(mProdDriverPackageName); @@ -117,20 +117,20 @@ public class GpuService extends SystemService { } mSettingsObserver = new SettingsObserver(); mDeviceConfigListener = new DeviceConfigListener(); - fetchGameDriverPackageProperties(); + fetchProductionDriverPackageProperties(); processDenylists(); setDenylist(); - fetchDeveloperDriverPackageProperties(); + fetchPrereleaseDriverPackageProperties(); } } private final class SettingsObserver extends ContentObserver { - private final Uri mGameDriverDenylistsUri = - Settings.Global.getUriFor(Settings.Global.GAME_DRIVER_DENYLISTS); + private final Uri mProdDriverDenylistsUri = + Settings.Global.getUriFor(Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLISTS); SettingsObserver() { super(new Handler()); - mContentResolver.registerContentObserver(mGameDriverDenylistsUri, false, this, + mContentResolver.registerContentObserver(mProdDriverDenylistsUri, false, this, UserHandle.USER_ALL); } @@ -140,7 +140,7 @@ public class GpuService extends SystemService { return; } - if (mGameDriverDenylistsUri.equals(uri)) { + if (mProdDriverDenylistsUri.equals(uri)) { processDenylists(); setDenylist(); } @@ -157,9 +157,11 @@ public class GpuService extends SystemService { @Override public void onPropertiesChanged(Properties properties) { synchronized (mDeviceConfigLock) { - if (properties.getKeyset().contains(Settings.Global.GAME_DRIVER_DENYLISTS)) { + if (properties.getKeyset().contains( + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLISTS)) { parseDenylists( - properties.getString(Settings.Global.GAME_DRIVER_DENYLISTS, "")); + properties.getString( + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLISTS, "")); setDenylist(); } } @@ -186,10 +188,10 @@ public class GpuService extends SystemService { case ACTION_PACKAGE_CHANGED: case ACTION_PACKAGE_REMOVED: if (isProdDriver) { - fetchGameDriverPackageProperties(); + fetchProductionDriverPackageProperties(); setDenylist(); } else if (isDevDriver) { - fetchDeveloperDriverPackageProperties(); + fetchPrereleaseDriverPackageProperties(); } break; default: @@ -218,7 +220,7 @@ public class GpuService extends SystemService { } } - private void fetchGameDriverPackageProperties() { + private void fetchProductionDriverPackageProperties() { final ApplicationInfo driverInfo; try { driverInfo = mPackageManager.getApplicationInfo(mProdDriverPackageName, @@ -241,15 +243,16 @@ public class GpuService extends SystemService { // Reset the allowlist. Settings.Global.putString(mContentResolver, - Settings.Global.GAME_DRIVER_ALLOWLIST, ""); - mGameDriverVersionCode = driverInfo.longVersionCode; + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST, ""); + mProdDriverVersionCode = driverInfo.longVersionCode; try { final Context driverContext = mContext.createPackageContext(mProdDriverPackageName, Context.CONTEXT_RESTRICTED); - assetToSettingsGlobal(mContext, driverContext, GAME_DRIVER_ALLOWLIST_FILENAME, - Settings.Global.GAME_DRIVER_ALLOWLIST, ","); + assetToSettingsGlobal(mContext, driverContext, + UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST_FILENAME, + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST, ","); } catch (PackageManager.NameNotFoundException e) { if (DEBUG) { Slog.w(TAG, "driver package '" + mProdDriverPackageName + "' not installed"); @@ -259,11 +262,11 @@ public class GpuService extends SystemService { private void processDenylists() { String base64String = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_GAME_DRIVER, - Settings.Global.GAME_DRIVER_DENYLISTS); + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLISTS); if (base64String == null) { base64String = Settings.Global.getString(mContentResolver, - Settings.Global.GAME_DRIVER_DENYLISTS); + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLISTS); } parseDenylists(base64String != null ? base64String : ""); } @@ -288,16 +291,16 @@ public class GpuService extends SystemService { private void setDenylist() { Settings.Global.putString(mContentResolver, - Settings.Global.GAME_DRIVER_DENYLIST, ""); + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST, ""); synchronized (mLock) { if (mDenylists == null) { return; } List<Denylist> denylists = mDenylists.getDenylistsList(); for (Denylist denylist : denylists) { - if (denylist.getVersionCode() == mGameDriverVersionCode) { + if (denylist.getVersionCode() == mProdDriverVersionCode) { Settings.Global.putString(mContentResolver, - Settings.Global.GAME_DRIVER_DENYLIST, + Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST, String.join(",", denylist.getPackageNamesList())); return; } @@ -305,7 +308,7 @@ public class GpuService extends SystemService { } } - private void fetchDeveloperDriverPackageProperties() { + private void fetchPrereleaseDriverPackageProperties() { final ApplicationInfo driverInfo; try { driverInfo = mPackageManager.getApplicationInfo(mDevDriverPackageName, diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java index 7bbcdaa2d473..6672daa6f17a 100644 --- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java +++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java @@ -949,7 +949,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem if (debug) { Slog.d(mTag, "Eagerly recreating service for user " + userId); } - getServiceForUserLocked(userId); + updateCachedServiceLocked(userId); } } onServicePackageRestartedLocked(userId); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java index 05cf40a091b6..3ac95d71de3d 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java @@ -178,7 +178,7 @@ public abstract class InputMethodManagerInternal { }; /** - * @return Global instance if exists. Otherwise, a dummy no-op instance. + * @return Global instance if exists. Otherwise, a fallback no-op instance. */ @NonNull public static InputMethodManagerInternal get() { diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 3cd70fecbf3d..9ab410d258cc 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -2213,8 +2213,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub * @param client {@link android.os.Binder} proxy that is associated with the singleton instance * of {@link android.view.inputmethod.InputMethodManager} that runs on the client * process - * @param inputContext communication channel for the dummy - * {@link android.view.inputmethod.InputConnection} + * @param inputContext communication channel for the fallback {@link InputConnection} * @param selfReportedDisplayId self-reported display ID to which the client is associated. * Whether the client is still allowed to access to this display * or not needs to be evaluated every time the client interacts @@ -3451,9 +3450,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return InputBindResult.USER_SWITCHING; } - // Master feature flag that overrides other conditions and forces IME preRendering. + // Main feature flag that overrides other conditions and forces IME preRendering. if (DEBUG) { - Slog.v(TAG, "IME PreRendering MASTER flag: " + Slog.v(TAG, "IME PreRendering main flag: " + DebugFlags.FLAG_PRE_RENDER_IME_VIEWS.value() + ", LowRam: " + mIsLowRam); } // pre-rendering not supported on low-ram devices. diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 937514ca9139..b518eb1ab6d0 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -1663,7 +1663,7 @@ public final class MultiClientInputMethodManagerService { } if (editorInfo == null) { - // So-called dummy InputConnection scenario. For app compatibility, we still + // So-called fallback InputConnection scenario. For app compatibility, we still // notify this to the IME. switch (clientInfo.mState) { case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT: diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java index c3532a84c42d..05aa3150cfef 100644 --- a/services/core/java/com/android/server/location/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/LocationProviderManager.java @@ -229,7 +229,7 @@ class LocationProviderManager extends // we cache these values because checking/calculating on the fly is more expensive private boolean mPermitted; private boolean mForeground; - @Nullable private LocationRequest mProviderLocationRequest; + private LocationRequest mProviderLocationRequest; private boolean mIsUsingHighPower; protected Registration(LocationRequest request, CallerIdentity identity, @@ -244,6 +244,8 @@ class LocationProviderManager extends } else { mWorkSource = identity.addToWorkSource(null); } + + mProviderLocationRequest = super.getRequest(); } @GuardedBy("mLock") @@ -313,7 +315,7 @@ class LocationProviderManager extends @Override public final LocationRequest getRequest() { - return Objects.requireNonNull(mProviderLocationRequest); + return mProviderLocationRequest; } public final boolean isForeground() { diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java new file mode 100644 index 000000000000..e3074dba26ef --- /dev/null +++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2020 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 com.android.server.locksettings; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.hardware.face.FaceManager; +import android.hardware.face.FaceSensorProperties; +import android.hardware.fingerprint.FingerprintManager; +import android.hardware.fingerprint.FingerprintSensorProperties; +import android.os.Handler; +import android.os.IBinder; +import android.os.ServiceManager; +import android.service.gatekeeper.IGateKeeperService; +import android.util.ArraySet; +import android.util.Slog; + +import com.android.internal.widget.VerifyCredentialResponse; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Class that handles biometric-related work in the {@link LockSettingsService} area, for example + * resetLockout. + */ +@SuppressWarnings("deprecation") +public class BiometricDeferredQueue { + private static final String TAG = "BiometricDeferredQueue"; + + @NonNull private final Context mContext; + @NonNull private final SyntheticPasswordManager mSpManager; + @NonNull private final Handler mHandler; + @Nullable private FingerprintManager mFingerprintManager; + @Nullable private FaceManager mFaceManager; + + // Entries added by LockSettingsService once a user's synthetic password is known. At this point + // things are still keyed by userId. + @NonNull private final ArrayList<UserAuthInfo> mPendingResetLockouts; + + /** + * Authentication info for a successful user unlock via Synthetic Password. This can be used to + * perform multiple operations (e.g. resetLockout for multiple HALs/Sensors) by sending the + * Gatekeeper Password to Gatekeer multiple times, each with a sensor-specific challenge. + */ + private static class UserAuthInfo { + final int userId; + @NonNull final byte[] gatekeeperPassword; + + UserAuthInfo(int userId, @NonNull byte[] gatekeeperPassword) { + this.userId = userId; + this.gatekeeperPassword = gatekeeperPassword; + } + } + + /** + * Per-authentication callback. + */ + private static class FaceResetLockoutTask implements FaceManager.GenerateChallengeCallback { + interface FinishCallback { + void onFinished(); + } + + @NonNull FinishCallback finishCallback; + @NonNull FaceManager faceManager; + @NonNull SyntheticPasswordManager spManager; + @NonNull Set<Integer> sensorIds; // IDs of sensors waiting for challenge + @NonNull List<UserAuthInfo> pendingResetLockuts; + + FaceResetLockoutTask( + @NonNull FinishCallback finishCallback, + @NonNull FaceManager faceManager, + @NonNull SyntheticPasswordManager spManager, + @NonNull Set<Integer> sensorIds, + @NonNull List<UserAuthInfo> pendingResetLockouts) { + this.finishCallback = finishCallback; + this.faceManager = faceManager; + this.spManager = spManager; + this.sensorIds = sensorIds; + this.pendingResetLockuts = pendingResetLockouts; + } + + @Override + public void onChallengeInterrupted(int sensorId) { + Slog.w(TAG, "Challenge interrupted, sensor: " + sensorId); + // Consider re-attempting generateChallenge/resetLockout/revokeChallenge + // when onChallengeInterruptFinished is invoked + } + + @Override + public void onChallengeInterruptFinished(int sensorId) { + Slog.w(TAG, "Challenge interrupt finished, sensor: " + sensorId); + } + + @Override + public void onGenerateChallengeResult(int sensorId, long challenge) { + if (!sensorIds.contains(sensorId)) { + Slog.e(TAG, "Unknown sensorId received: " + sensorId); + return; + } + + // Challenge received for a sensor. For each sensor, reset lockout for all users. + for (UserAuthInfo userAuthInfo : pendingResetLockuts) { + Slog.d(TAG, "Resetting face lockout for sensor: " + sensorId + + ", user: " + userAuthInfo.userId); + final VerifyCredentialResponse response = spManager.verifyChallengeInternal( + getGatekeeperService(), userAuthInfo.gatekeeperPassword, challenge, + userAuthInfo.userId); + if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) { + Slog.wtf(TAG, "VerifyChallenge failed, response: " + + response.getResponseCode()); + } + faceManager.resetLockout(sensorId, userAuthInfo.userId, + response.getGatekeeperHAT()); + } + + sensorIds.remove(sensorId); + faceManager.revokeChallenge(sensorId); + + if (sensorIds.isEmpty()) { + Slog.d(TAG, "Done requesting resetLockout for all face sensors"); + finishCallback.onFinished(); + } + } + + synchronized IGateKeeperService getGatekeeperService() { + final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE); + if (service == null) { + Slog.e(TAG, "Unable to acquire GateKeeperService"); + return null; + } + return IGateKeeperService.Stub.asInterface(service); + } + } + + @Nullable private FaceResetLockoutTask mFaceResetLockoutTask; + + private final FaceResetLockoutTask.FinishCallback mFaceFinishCallback = () -> { + mFaceResetLockoutTask = null; + }; + + BiometricDeferredQueue(@NonNull Context context, @NonNull SyntheticPasswordManager spManager, + @NonNull Handler handler) { + mContext = context; + mSpManager = spManager; + mHandler = handler; + mPendingResetLockouts = new ArrayList<>(); + } + + public void systemReady(@Nullable FingerprintManager fingerprintManager, + @Nullable FaceManager faceManager) { + mFingerprintManager = fingerprintManager; + mFaceManager = faceManager; + } + + /** + * Adds a request for resetLockout on all biometric sensors for the user specified. The queue + * owner must invoke {@link #processPendingLockoutResets()} at some point to kick off the + * operations. + * + * Note that this should only ever be invoked for successful authentications, otherwise it will + * consume a Gatekeeper authentication attempt and potentially wipe the user/device. + * + * @param userId The user that the operation will apply for. + * @param gatekeeperPassword The Gatekeeper Password + */ + void addPendingLockoutResetForUser(int userId, @NonNull byte[] gatekeeperPassword) { + mHandler.post(() -> { + Slog.d(TAG, "addPendingLockoutResetForUser: " + userId); + mPendingResetLockouts.add(new UserAuthInfo(userId, gatekeeperPassword)); + }); + } + + void processPendingLockoutResets() { + mHandler.post(() -> { + Slog.d(TAG, "processPendingLockoutResets: " + mPendingResetLockouts.size()); + processPendingLockoutsForFingerprint(new ArrayList<>(mPendingResetLockouts)); + processPendingLockoutsForFace(new ArrayList<>(mPendingResetLockouts)); + mPendingResetLockouts.clear(); + }); + } + + private void processPendingLockoutsForFingerprint(List<UserAuthInfo> pendingResetLockouts) { + if (mFingerprintManager != null) { + final List<FingerprintSensorProperties> fingerprintSensorProperties = + mFingerprintManager.getSensorProperties(); + for (FingerprintSensorProperties prop : fingerprintSensorProperties) { + if (!prop.resetLockoutRequiresHardwareAuthToken) { + for (UserAuthInfo user : pendingResetLockouts) { + mFingerprintManager.resetLockout(prop.sensorId, user.userId, + null /* hardwareAuthToken */); + } + } else { + Slog.e(TAG, "Fingerprint resetLockout with HAT not supported yet"); + // TODO(b/152414803): Implement this when resetLockout is implemented below + // the framework. + } + } + } + } + + /** + * For devices on {@link android.hardware.biometrics.face.V1_0} which only support a single + * in-flight challenge, we generate a single challenge to reset lockout for all profiles. This + * hopefully reduces/eliminates issues such as overwritten challenge, incorrectly revoked + * challenge, or other race conditions. + * + * TODO(b/162965646) This logic can be avoided if multiple in-flight challenges are supported. + * Though it will need to continue to exist to support existing HIDLs, each profile that + * requires resetLockout could have its own challenge, and the `mPendingResetLockouts` queue + * can be avoided. + */ + private void processPendingLockoutsForFace(List<UserAuthInfo> pendingResetLockouts) { + if (mFaceManager != null) { + if (mFaceResetLockoutTask != null) { + // This code will need to be updated if this problem ever occurs. + Slog.w(TAG, "mFaceGenerateChallengeCallback not null, previous operation may be" + + " stuck"); + } + final List<FaceSensorProperties> faceSensorProperties = + mFaceManager.getSensorProperties(); + final Set<Integer> sensorIds = new ArraySet<>(); + for (FaceSensorProperties prop : faceSensorProperties) { + sensorIds.add(prop.sensorId); + } + + mFaceResetLockoutTask = new FaceResetLockoutTask(mFaceFinishCallback, mFaceManager, + mSpManager, sensorIds, pendingResetLockouts); + for (final FaceSensorProperties prop : faceSensorProperties) { + // Generate a challenge for each sensor. The challenge does not need to be + // per-user, since the HAT returned by gatekeeper contains userId. + mFaceManager.generateChallenge(prop.sensorId, mFaceResetLockoutTask); + } + } + } +} diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index d6e37bacdba8..0044d8936841 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -37,7 +37,6 @@ import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_RETURN_GK import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled; import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -187,23 +186,6 @@ public class LockSettingsService extends ILockSettings.Stub { private static final String SYNTHETIC_PASSWORD_UPDATE_TIME_KEY = "sp-handle-ts"; private static final String USER_SERIAL_NUMBER_KEY = "serial-number"; - // TODO (b/145978626) LockSettingsService no longer accepts challenges in the verifyCredential - // paths. These are temporarily left around to ensure that resetLockout works. It will be - // removed once resetLockout is compartmentalized. - // No challenge provided - private static final int CHALLENGE_NONE = 0; - // Challenge was provided from the external caller (non-LockSettingsService) - private static final int CHALLENGE_FROM_CALLER = 1; - // Challenge was generated from within LockSettingsService, for resetLockout. When challenge - // type is set to internal, LSS will revokeChallenge after all profiles for that user are - // unlocked. - private static final int CHALLENGE_INTERNAL = 2; - - @IntDef({CHALLENGE_NONE, - CHALLENGE_FROM_CALLER, - CHALLENGE_INTERNAL}) - @interface ChallengeType {} - // Order of holding lock: mSeparateChallengeLock -> mSpManager -> this // Do not call into ActivityManager while holding mSpManager lock. private final Object mSeparateChallengeLock = new Object(); @@ -219,6 +201,7 @@ public class LockSettingsService extends ILockSettings.Stub { protected final LockSettingsStorage mStorage; private final LockSettingsStrongAuth mStrongAuth; private final SynchronizedStrongAuthTracker mStrongAuthTracker; + private final BiometricDeferredQueue mBiometricDeferredQueue; private final NotificationManager mNotificationManager; private final UserManager mUserManager; @@ -321,15 +304,6 @@ public class LockSettingsService extends ILockSettings.Stub { } } - private class PendingResetLockout { - final int mUserId; - final byte[] mHAT; - PendingResetLockout(int userId, byte[] hat) { - mUserId = userId; - mHAT = hat; - } - } - private LockscreenCredential generateRandomProfilePassword() { byte[] randomLockSeed = new byte[] {}; try { @@ -588,6 +562,7 @@ public class LockSettingsService extends ILockSettings.Stub { mSpManager = injector.getSyntheticPasswordManager(mStorage); mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(); + mBiometricDeferredQueue = new BiometricDeferredQueue(mContext, mSpManager, mHandler); mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(), mStorage); @@ -740,8 +715,7 @@ public class LockSettingsService extends ILockSettings.Stub { // If boot took too long and the password in vold got expired, parent keystore will // be still locked, we ignore this case since the user will be prompted to unlock // the device after boot. - unlockChildProfile(userId, true /* ignoreUserNotAuthenticated */, - CHALLENGE_NONE, 0 /* challenge */, null /* resetLockouts */); + unlockChildProfile(userId, true /* ignoreUserNotAuthenticated */); } } @@ -830,6 +804,8 @@ public class LockSettingsService extends ILockSettings.Stub { mRebootEscrowManager.loadRebootEscrowDataIfAvailable(); // TODO: maybe skip this for split system user mode. mStorage.prefetchUser(UserHandle.USER_SYSTEM); + mBiometricDeferredQueue.systemReady(mInjector.getFingerprintManager(), + mInjector.getFaceManager()); } private void getAuthSecretHal() { @@ -1306,13 +1282,10 @@ public class LockSettingsService extends ILockSettings.Stub { return credential; } - private void unlockChildProfile(int profileHandle, boolean ignoreUserNotAuthenticated, - @ChallengeType int challengeType, long challenge, - @Nullable ArrayList<PendingResetLockout> resetLockouts) { + private void unlockChildProfile(int profileHandle, boolean ignoreUserNotAuthenticated) { try { doVerifyCredential(getDecryptedPasswordForTiedProfile(profileHandle), - challengeType, challenge, profileHandle, null /* progressCallback */, - resetLockouts, 0 /* flags */); + profileHandle, null /* progressCallback */, 0 /* flags */); } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException | IllegalBlockSizeException @@ -1327,10 +1300,6 @@ public class LockSettingsService extends ILockSettings.Stub { } } - private void unlockUser(int userId, byte[] token, byte[] secret) { - unlockUser(userId, token, secret, CHALLENGE_NONE, 0 /* challenge */, null); - } - /** * Unlock the user (both storage and user state) and its associated managed profiles * synchronously. @@ -1339,9 +1308,7 @@ public class LockSettingsService extends ILockSettings.Stub { * can end up calling into other system services to process user unlock request (via * {@link com.android.server.SystemServiceManager#unlockUser} </em> */ - private void unlockUser(int userId, byte[] token, byte[] secret, - @ChallengeType int challengeType, long challenge, - @Nullable ArrayList<PendingResetLockout> resetLockouts) { + private void unlockUser(int userId, byte[] token, byte[] secret) { Slog.i(TAG, "Unlocking user " + userId + " with secret only, length " + (secret != null ? secret.length : 0)); // TODO: make this method fully async so we can update UI with progress strings @@ -1378,6 +1345,9 @@ public class LockSettingsService extends ILockSettings.Stub { } if (mUserManager.getUserInfo(userId).isManagedProfile()) { + if (!hasUnifiedChallenge(userId)) { + mBiometricDeferredQueue.processPendingLockoutResets(); + } return; } @@ -1388,10 +1358,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (hasUnifiedChallenge(profile.id)) { if (mUserManager.isUserRunning(profile.id)) { // Unlock managed profile with unified lock - // Must pass the challenge on for resetLockout, so it's not over-written, which - // causes LockSettingsService to revokeChallenge inappropriately. - unlockChildProfile(profile.id, false /* ignoreUserNotAuthenticated */, - challengeType, challenge, resetLockouts); + unlockChildProfile(profile.id, false /* ignoreUserNotAuthenticated */); } else { try { // Profile not ready for unlock yet, but decrypt the unified challenge now @@ -1412,22 +1379,9 @@ public class LockSettingsService extends ILockSettings.Stub { restoreCallingIdentity(ident); } } - } - if (resetLockouts != null && !resetLockouts.isEmpty()) { - mHandler.post(() -> { - final BiometricManager bm = mContext.getSystemService(BiometricManager.class); - final PackageManager pm = mContext.getPackageManager(); - for (int i = 0; i < resetLockouts.size(); i++) { - bm.resetLockout(resetLockouts.get(i).mUserId, resetLockouts.get(i).mHAT); - } - if (challengeType == CHALLENGE_INTERNAL - && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) { - mContext.getSystemService(FaceManager.class).revokeChallenge(); - } - }); - } + mBiometricDeferredQueue.processPendingLockoutResets(); } private boolean hasUnifiedChallenge(int userId) { @@ -1727,8 +1681,7 @@ public class LockSettingsService extends ILockSettings.Stub { setUserKeyProtection(userId, credential, convertResponse(gkResponse)); fixateNewestUserKeyAuth(userId); // Refresh the auth token - doVerifyCredential(credential, CHALLENGE_FROM_CALLER, 0, userId, - null /* progressCallback */, 0 /* flags */); + doVerifyCredential(credential, userId, null /* progressCallback */, 0 /* flags */); synchronizeUnifiedWorkChallengeForProfiles(userId, null); sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent); return true; @@ -1972,8 +1925,7 @@ public class LockSettingsService extends ILockSettings.Stub { ICheckCredentialProgressCallback progressCallback) { checkPasswordReadPermission(userId); try { - return doVerifyCredential(credential, CHALLENGE_NONE, 0L, userId, progressCallback, - 0 /* flags */); + return doVerifyCredential(credential, userId, progressCallback, 0 /* flags */); } finally { scheduleGc(); } @@ -1984,10 +1936,8 @@ public class LockSettingsService extends ILockSettings.Stub { public VerifyCredentialResponse verifyCredential(LockscreenCredential credential, int userId, int flags) { checkPasswordReadPermission(userId); - try { - return doVerifyCredential(credential, CHALLENGE_NONE, 0L, userId, - null /* progressCallback */, flags); + return doVerifyCredential(credential, userId, null /* progressCallback */, flags); } finally { scheduleGc(); } @@ -2006,32 +1956,17 @@ public class LockSettingsService extends ILockSettings.Stub { return response; } - /** + /* + * Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero + * format. * @param credential User's lockscreen credential - * @param challengeType Owner of the challenge - * @param challenge Challenge to be wrapped within Gatekeeper's HAT, if the credential is - * verified * @param userId User to verify the credential for * @param progressCallback Receive progress callbacks * @param flags See {@link LockPatternUtils.VerifyFlag} * @return See {@link VerifyCredentialResponse} */ private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential, - @ChallengeType int challengeType, long challenge, int userId, - ICheckCredentialProgressCallback progressCallback, - @LockPatternUtils.VerifyFlag int flags) { - return doVerifyCredential(credential, challengeType, challenge, userId, - progressCallback, null /* resetLockouts */, flags); - } - - /** - * Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero - * format. - */ - private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential, - @ChallengeType int challengeType, long challenge, int userId, - ICheckCredentialProgressCallback progressCallback, - @Nullable ArrayList<PendingResetLockout> resetLockouts, + int userId, ICheckCredentialProgressCallback progressCallback, @LockPatternUtils.VerifyFlag int flags) { if (credential == null || credential.isNone()) { throw new IllegalArgumentException("Credential can't be null or empty"); @@ -2041,9 +1976,10 @@ public class LockSettingsService extends ILockSettings.Stub { Slog.e(TAG, "FRP credential can only be verified prior to provisioning."); return VerifyCredentialResponse.ERROR; } - VerifyCredentialResponse response = null; - response = spBasedDoVerifyCredential(credential, challengeType, challenge, - userId, progressCallback, resetLockouts, flags); + + VerifyCredentialResponse response = spBasedDoVerifyCredential(credential, userId, + progressCallback, flags); + // The user employs synthetic password based credential. if (response != null) { if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { @@ -2064,8 +2000,7 @@ public class LockSettingsService extends ILockSettings.Stub { return VerifyCredentialResponse.ERROR; } - response = verifyCredential(userId, storedHash, credential, - challengeType, challenge, progressCallback); + response = verifyCredential(userId, storedHash, credential, progressCallback); if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { mStrongAuth.reportSuccessfulStrongAuthUnlock(userId); @@ -2085,8 +2020,6 @@ public class LockSettingsService extends ILockSettings.Stub { // Unlock parent by using parent's challenge final VerifyCredentialResponse parentResponse = doVerifyCredential( credential, - CHALLENGE_NONE, - 0L, parentProfileId, null /* progressCallback */, flags); @@ -2098,10 +2031,7 @@ public class LockSettingsService extends ILockSettings.Stub { try { // Unlock work profile, and work profile with unified lock must use password only return doVerifyCredential(getDecryptedPasswordForTiedProfile(userId), - CHALLENGE_NONE, - 0L, - userId, null /* progressCallback */, - flags); + userId, null /* progressCallback */, flags); } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException | IllegalBlockSizeException @@ -2119,8 +2049,7 @@ public class LockSettingsService extends ILockSettings.Stub { * hash to GK. */ private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash, - LockscreenCredential credential, @ChallengeType int challengeType, long challenge, - ICheckCredentialProgressCallback progressCallback) { + LockscreenCredential credential, ICheckCredentialProgressCallback progressCallback) { if ((storedHash == null || storedHash.hash.length == 0) && credential.isNone()) { // don't need to pass empty credentials to GateKeeper return VerifyCredentialResponse.OK; @@ -2137,7 +2066,7 @@ public class LockSettingsService extends ILockSettings.Stub { GateKeeperResponse gateKeeperResponse; try { gateKeeperResponse = getGateKeeperService().verifyChallenge( - userId, challenge, storedHash.hash, credential.getCredential()); + userId, 0L /* challenge */, storedHash.hash, credential.getCredential()); } catch (RemoteException e) { Slog.e(TAG, "gatekeeper verify failed", e); gateKeeperResponse = GateKeeperResponse.ERROR; @@ -2710,28 +2639,13 @@ public class LockSettingsService extends ILockSettings.Stub { } private VerifyCredentialResponse spBasedDoVerifyCredential(LockscreenCredential userCredential, - @ChallengeType int challengeType, long challenge, int userId, ICheckCredentialProgressCallback progressCallback, - @Nullable ArrayList<PendingResetLockout> resetLockouts, @LockPatternUtils.VerifyFlag int flags) { - final boolean hasEnrolledBiometrics = mInjector.hasEnrolledBiometrics(userId); - Slog.d(TAG, "spBasedDoVerifyCredential: user=" + userId + " challengeType=" + challengeType + Slog.d(TAG, "spBasedDoVerifyCredential: user=" + userId + " hasEnrolledBiometrics=" + hasEnrolledBiometrics); - final PackageManager pm = mContext.getPackageManager(); - // TODO: When lockout is handled under the HAL for all biometrics (fingerprint), - // we need to generate challenge for each one, have it signed by GK and reset lockout - // for each modality. - if (challengeType == CHALLENGE_NONE && pm.hasSystemFeature(PackageManager.FEATURE_FACE) - && hasEnrolledBiometrics) { - // If there are multiple profiles in the same account, ensure we only generate the - // challenge once. - challengeType = CHALLENGE_INTERNAL; - challenge = mContext.getSystemService(FaceManager.class).generateChallengeBlocking(); - } - final AuthenticationResult authResult; VerifyCredentialResponse response; final boolean returnGkPw = (flags & VERIFY_FLAG_RETURN_GK_PW) != 0; @@ -2752,10 +2666,13 @@ public class LockSettingsService extends ILockSettings.Stub { // credential has matched if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { + mBiometricDeferredQueue.addPendingLockoutResetForUser(userId, + authResult.authToken.deriveGkPassword()); + // perform verifyChallenge with synthetic password which generates the real GK auth // token and response for the current user response = mSpManager.verifyChallenge(getGateKeeperService(), authResult.authToken, - challenge, userId); + 0L /* challenge */, userId); if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) { // This shouldn't really happen: the unwrapping of SP succeeds, but SP doesn't // match the recorded GK password handle. @@ -2765,15 +2682,7 @@ public class LockSettingsService extends ILockSettings.Stub { } } if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { - // Do resetLockout / revokeChallenge when all profiles are unlocked - if (hasEnrolledBiometrics) { - if (resetLockouts == null) { - resetLockouts = new ArrayList<>(); - } - resetLockouts.add(new PendingResetLockout(userId, response.getGatekeeperHAT())); - } - - onCredentialVerified(authResult.authToken, challengeType, challenge, resetLockouts, + onCredentialVerified(authResult.authToken, PasswordMetrics.computeForCredential(userCredential), userId); } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) { if (response.getTimeout() > 0) { @@ -2789,9 +2698,7 @@ public class LockSettingsService extends ILockSettings.Stub { } } - private void onCredentialVerified(AuthenticationToken authToken, - @ChallengeType int challengeType, long challenge, - @Nullable ArrayList<PendingResetLockout> resetLockouts, PasswordMetrics metrics, + private void onCredentialVerified(AuthenticationToken authToken, PasswordMetrics metrics, int userId) { if (metrics != null) { @@ -2806,7 +2713,7 @@ public class LockSettingsService extends ILockSettings.Stub { { final byte[] secret = authToken.deriveDiskEncryptionKey(); - unlockUser(userId, null, secret, challengeType, challenge, resetLockouts); + unlockUser(userId, null, secret); Arrays.fill(secret, (byte) 0); } activateEscrowTokens(authToken, userId); @@ -3193,11 +3100,8 @@ public class LockSettingsService extends ILockSettings.Stub { return false; } } - // TODO: Reset biometrics lockout here. Ideally that should be self-contained inside - // onCredentialVerified(), which will require some refactoring on the current lockout - // reset logic. - onCredentialVerified(authResult.authToken, CHALLENGE_NONE, 0, null, + onCredentialVerified(authResult.authToken, loadPasswordMetrics(authResult.authToken, userId), userId); return true; } @@ -3208,8 +3112,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (cred == null) { return false; } - return doVerifyCredential(cred, CHALLENGE_NONE, 0, userId, - null /* progressCallback */, 0 /* flags */) + return doVerifyCredential(cred, userId, null /* progressCallback */, 0 /* flags */) .getResponseCode() == VerifyCredentialResponse.RESPONSE_OK; } } @@ -3532,8 +3435,7 @@ public class LockSettingsService extends ILockSettings.Stub { SyntheticPasswordManager.AuthenticationToken authToken = new SyntheticPasswordManager.AuthenticationToken(spVersion); authToken.recreateDirectly(syntheticPassword); - onCredentialVerified(authToken, CHALLENGE_NONE, 0, null, - loadPasswordMetrics(authToken, userId), userId); + onCredentialVerified(authToken, loadPasswordMetrics(authToken, userId), userId); } } } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java index 81d07cc11527..e9a05a8aa16c 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java @@ -484,7 +484,7 @@ class LockSettingsStorage { public Map<Integer, List<Long>> listSyntheticPasswordHandlesForAllUsers(String stateName) { Map<Integer, List<Long>> result = new ArrayMap<>(); final UserManager um = UserManager.get(mContext); - for (UserInfo user : um.getUsers(false)) { + for (UserInfo user : um.getUsers()) { result.put(user.id, listSyntheticPasswordHandlesForUser(stateName, user.id)); } return result; diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java index 3a4dfaf9bfcd..0b3cdae9231e 100644 --- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java +++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java @@ -34,6 +34,7 @@ import android.content.IntentFilter; import android.media.AudioManager; import android.media.AudioSystem; import android.media.MediaRoute2Info; +import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.util.Slog; @@ -55,7 +56,6 @@ class BluetoothRouteProvider { private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_"; - private static BluetoothRouteProvider sInstance; @SuppressWarnings("WeakerAccess") /* synthetic access */ // Maps hardware address to BluetoothRouteInfo @@ -79,19 +79,21 @@ class BluetoothRouteProvider { private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver(); private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener(); - static synchronized BluetoothRouteProvider getInstance(@NonNull Context context, + /** + * Create an instance of {@link BluetoothRouteProvider}. + * It may return {@code null} if Bluetooth is not supported on this hardware platform. + */ + @Nullable + static BluetoothRouteProvider createInstance(@NonNull Context context, @NonNull BluetoothRoutesUpdatedListener listener) { Objects.requireNonNull(context); Objects.requireNonNull(listener); - if (sInstance == null) { - BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); - if (btAdapter == null) { - return null; - } - sInstance = new BluetoothRouteProvider(context, btAdapter, listener); + BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); + if (btAdapter == null) { + return null; } - return sInstance; + return new BluetoothRouteProvider(context, btAdapter, listener); } private BluetoothRouteProvider(Context context, BluetoothAdapter btAdapter, @@ -103,7 +105,7 @@ class BluetoothRouteProvider { buildBluetoothRoutes(); } - public void start() { + public void start(UserHandle user) { mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP); mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID); @@ -118,7 +120,8 @@ class BluetoothRouteProvider { addEventReceiver(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED, deviceStateChangedReceiver); - mContext.registerReceiver(mBroadcastReceiver, mIntentFilter, null, null); + mContext.registerReceiverAsUser(mBroadcastReceiver, user, + mIntentFilter, null, null); } /** diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 875bfdffafcd..1114fe0d9bf8 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -1176,7 +1176,8 @@ class MediaRouter2ServiceImpl { super(Looper.getMainLooper(), null, true); mServiceRef = new WeakReference<>(service); mUserRecord = userRecord; - mSystemProvider = new SystemMediaRoute2Provider(service.mContext); + mSystemProvider = new SystemMediaRoute2Provider(service.mContext, + UserHandle.of(userRecord.mUserId)); mRouteProviders.add(mSystemProvider); mWatcher = new MediaRoute2ProviderWatcher(service.mContext, this, this, mUserRecord.mUserId); diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 0f6748366e16..8777ceacf884 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -589,11 +589,19 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR if (mDestroyed) { return; } + ParceledListSlice<QueueItem> parcelableQueue; + if (mQueue == null) { + parcelableQueue = null; + } else { + parcelableQueue = new ParceledListSlice<>(mQueue); + // Limit the size of initial Parcel to prevent binder buffer overflow + // as onQueueChanged is an async binder call. + parcelableQueue.setInlineCountLimit(1); + } for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); try { - holder.mCallback.onQueueChanged(mQueue == null ? null : - new ParceledListSlice<>(mQueue)); + holder.mCallback.onQueueChanged(parcelableQueue); } catch (DeadObjectException e) { mControllerCallbackHolders.remove(i); logCallbackException("Removing dead callback in pushQueueUpdate", holder, e); diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index e2f70e320cb8..9521611c241d 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -32,14 +32,15 @@ import android.app.KeyguardManager; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ActivityNotFoundException; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; -import android.database.ContentObserver; import android.media.AudioManager; import android.media.AudioManagerInternal; import android.media.AudioPlaybackConfiguration; @@ -58,7 +59,6 @@ import android.media.session.ISessionManager; import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.MediaSessionManager; -import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -138,7 +138,6 @@ public class MediaSessionService extends SystemService implements Monitor { private KeyguardManager mKeyguardManager; private AudioManagerInternal mAudioManagerInternal; private ContentResolver mContentResolver; - private SettingsObserver mSettingsObserver; private boolean mHasFeatureLeanback; // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile) @@ -192,8 +191,6 @@ public class MediaSessionService extends SystemService implements Monitor { } }, null /* handler */); mContentResolver = mContext.getContentResolver(); - mSettingsObserver = new SettingsObserver(); - mSettingsObserver.observe(); mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_LEANBACK); @@ -202,8 +199,20 @@ public class MediaSessionService extends SystemService implements Monitor { instantiateCustomProvider(null); instantiateCustomDispatcher(null); mRecordThread.start(); + + final IntentFilter filter = new IntentFilter( + NotificationManager.ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED); + mContext.registerReceiver(mNotificationListenerEnabledChangedReceiver, filter); } + private final BroadcastReceiver mNotificationListenerEnabledChangedReceiver = + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + updateActiveSessionListeners(); + } + }; + private boolean isGlobalPriorityActiveLocked() { return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive(); } @@ -1082,25 +1091,6 @@ public class MediaSessionService extends SystemService implements Monitor { } } - final class SettingsObserver extends ContentObserver { - private final Uri mSecureSettingsUri = Settings.Secure.getUriFor( - Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); - - private SettingsObserver() { - super(null); - } - - private void observe() { - mContentResolver.registerContentObserver(mSecureSettingsUri, - false, this, ALL.getIdentifier()); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - updateActiveSessionListeners(); - } - } - class SessionManagerImpl extends ISessionManager.Stub { private static final String EXTRA_WAKELOCK_ACQUIRED = "android.media.AudioService.WAKELOCK_ACQUIRED"; @@ -1928,7 +1918,7 @@ public class MediaSessionService extends SystemService implements Monitor { final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); final long token = Binder.clearCallingIdentity(); try { - // Don't perform sanity check between controllerPackageName and controllerUid. + // Don't perform check between controllerPackageName and controllerUid. // When an (activity|service) runs on the another apps process by specifying // android:process in the AndroidManifest.xml, then PID and UID would have the // running process' information instead of the (activity|service) that has created @@ -1937,7 +1927,8 @@ public class MediaSessionService extends SystemService implements Monitor { // Context#getPackageName() for getting package name that matches with the PID/UID, // but it doesn't tell which package has created the MediaController, so useless. return hasMediaControlPermission(controllerPid, controllerUid) - || hasEnabledNotificationListener(userId, controllerPackageName, uid); + || hasEnabledNotificationListener( + userId, controllerPackageName, controllerUid); } finally { Binder.restoreCallingIdentity(token); } @@ -2001,21 +1992,21 @@ public class MediaSessionService extends SystemService implements Monitor { return resolvedUserId; } - private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName, - int uid) { - // TODO: revisit this checking code - // You may not access another user's content as an enabled listener. - final int userId = UserHandle.getUserHandleForUid(resolvedUserId).getIdentifier(); - if (resolvedUserId != userId) { + private boolean hasEnabledNotificationListener(int callingUserId, + String controllerPackageName, int controllerUid) { + int controllerUserId = UserHandle.getUserHandleForUid(controllerUid).getIdentifier(); + if (callingUserId != controllerUserId) { + // Enabled notification listener only works within the same user. return false; } - if (mNotificationManager.hasEnabledNotificationListener(packageName, - UserHandle.getUserHandleForUid(uid))) { + + if (mNotificationManager.hasEnabledNotificationListener(controllerPackageName, + UserHandle.getUserHandleForUid(controllerUid))) { return true; } if (DEBUG) { - Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled " - + "notification listener"); + Log.d(TAG, controllerPackageName + " (uid=" + controllerUid + + ") doesn't have an enabled notification listener"); } return false; } @@ -2709,5 +2700,4 @@ public class MediaSessionService extends SystemService implements Monitor { obtainMessage(msg, userIdInteger).sendToTarget(); } } - } diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index 953aae44d6a7..d9b5b6d41c11 100644 --- a/services/core/java/com/android/server/media/MediaSessionStack.java +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -20,7 +20,6 @@ import static com.android.server.media.SessionPolicyProvider.SESSION_POLICY_IGNO import android.media.Session2Token; import android.media.session.MediaSession; -import android.os.Debug; import android.os.UserHandle; import android.util.Log; import android.util.SparseArray; @@ -187,7 +186,7 @@ class MediaSessionStack { */ public void updateMediaButtonSessionIfNeeded() { if (DEBUG) { - Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + Debug.getCallers(2)); + Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + getCallers(2)); } List<Integer> audioPlaybackUids = mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids(); @@ -413,4 +412,24 @@ class MediaSessionStack { // so they also need to be cleared. mCachedActiveLists.remove(UserHandle.USER_ALL); } + + // Code copied from android.os.Debug#getCallers(int) + private static String getCallers(final int depth) { + final StackTraceElement[] callStack = Thread.currentThread().getStackTrace(); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < depth; i++) { + sb.append(getCaller(callStack, i)).append(" "); + } + return sb.toString(); + } + + // Code copied from android.os.Debug#getCaller(StackTraceElement[], int) + private static String getCaller(StackTraceElement[] callStack, int depth) { + // callStack[4] is the caller of the method that called getCallers() + if (4 + depth >= callStack.length) { + return "<bottom of call stack>"; + } + StackTraceElement caller = callStack[4 + depth]; + return caller.getClassName() + "." + caller.getMethodName() + ":" + caller.getLineNumber(); + } } diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 2c089ca8300e..4f7af9469668 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -45,6 +45,7 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.util.Slog; @@ -99,7 +100,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } }; - SystemMediaRoute2Provider(Context context) { + SystemMediaRoute2Provider(Context context, UserHandle user) { super(sComponentName); mIsSystemRouteProvider = true; @@ -117,7 +118,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { updateDeviceRoute(newAudioRoutes); // .getInstance returns null if there is no bt adapter available - mBtRouteProvider = BluetoothRouteProvider.getInstance(context, (routes) -> { + mBtRouteProvider = BluetoothRouteProvider.createInstance(context, (routes) -> { publishProviderState(); boolean sessionInfoChanged; @@ -130,11 +131,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { IntentFilter intentFilter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION); intentFilter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION); - mContext.registerReceiver(new AudioManagerBroadcastReceiver(), intentFilter); + mContext.registerReceiverAsUser(new AudioManagerBroadcastReceiver(), user, + intentFilter, null, null); if (mBtRouteProvider != null) { mHandler.post(() -> { - mBtRouteProvider.start(); + mBtRouteProvider.start(user); notifyProviderState(); }); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 295143e76235..29ee8eb13564 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -4367,7 +4367,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // Flip state because app was explicitly added or removed to denylist. setMeteredNetworkDenylist(uid, (isDenylisted || isRestrictedByAdmin)); if (hasRule(oldRule, RULE_REJECT_METERED) && isAllowlisted) { - // Since dneylist prevails over allowlist, we need to handle the special case + // Since denylist prevails over allowlist, we need to handle the special case // where app is allowlisted and denylisted at the same time (although such // scenario should be blocked by the UI), then denylist is removed. setMeteredNetworkAllowlist(uid, isAllowlisted); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 2d052da22714..9f9235dc852f 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -31,6 +31,7 @@ import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL; import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED; import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED; +import static android.app.NotificationManager.ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED; import static android.app.NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED; import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_ID; @@ -7758,6 +7759,13 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting void updateUriPermissions(@Nullable NotificationRecord newRecord, @Nullable NotificationRecord oldRecord, String targetPkg, int targetUserId) { + updateUriPermissions(newRecord, oldRecord, targetPkg, targetUserId, false); + } + + @VisibleForTesting + void updateUriPermissions(@Nullable NotificationRecord newRecord, + @Nullable NotificationRecord oldRecord, String targetPkg, int targetUserId, + boolean onlyRevokeCurrentTarget) { final String key = (newRecord != null) ? newRecord.getKey() : oldRecord.getKey(); if (DBG) Slog.d(TAG, key + ": updating permissions"); @@ -7785,7 +7793,9 @@ public class NotificationManagerService extends SystemService { } // If we have no Uris to grant, but an existing owner, go destroy it - if (newUris == null && permissionOwner != null) { + // When revoking permissions of a single listener, destroying the owner will revoke + // permissions of other listeners who need to keep access. + if (newUris == null && permissionOwner != null && !onlyRevokeCurrentTarget) { destroyPermissionOwner(permissionOwner, UserHandle.getUserId(oldRecord.getUid()), key); permissionOwner = null; } @@ -7808,9 +7818,20 @@ public class NotificationManagerService extends SystemService { final Uri uri = oldUris.valueAt(i); if (newUris == null || !newUris.contains(uri)) { if (DBG) Slog.d(TAG, key + ": revoking " + uri); - int userId = ContentProvider.getUserIdFromUri( - uri, UserHandle.getUserId(oldRecord.getUid())); - revokeUriPermission(permissionOwner, uri, userId); + if (onlyRevokeCurrentTarget) { + // We're revoking permission from one listener only; other listeners may + // still need access because the notification may still exist + revokeUriPermission(permissionOwner, uri, + UserHandle.getUserId(oldRecord.getUid()), targetPkg, targetUserId); + } else { + // This is broad to unilaterally revoke permissions to this Uri as granted + // by this notification. But this code-path can only be used when the + // reason for revoking is that the notification posted again without this + // Uri, not when removing an individual listener. + revokeUriPermission(permissionOwner, uri, + UserHandle.getUserId(oldRecord.getUid()), + null, UserHandle.USER_ALL); + } } } } @@ -7839,8 +7860,10 @@ public class NotificationManagerService extends SystemService { } } - private void revokeUriPermission(IBinder owner, Uri uri, int userId) { + private void revokeUriPermission(IBinder owner, Uri uri, int sourceUserId, String targetPkg, + int targetUserId) { if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return; + int userId = ContentProvider.getUserIdFromUri(uri, sourceUserId); final long ident = Binder.clearCallingIdentity(); try { @@ -7848,7 +7871,7 @@ public class NotificationManagerService extends SystemService { owner, ContentProvider.getUriWithoutUserId(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION, - userId); + userId, targetPkg, targetUserId); } finally { Binder.restoreCallingIdentity(ident); } @@ -9158,6 +9181,17 @@ public class NotificationManagerService extends SystemService { } @Override + protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId, + boolean isPrimary, boolean enabled) { + super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled); + + getContext().sendBroadcastAsUser( + new Intent(ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), + UserHandle.ALL, null); + } + + @Override protected void loadDefaultsFromConfig() { String defaultListenerAccess = mContext.getResources().getString( R.string.config_defaultListenerAccessPackages); @@ -9219,6 +9253,7 @@ public class NotificationManagerService extends SystemService { final NotificationRankingUpdate update; synchronized (mNotificationLock) { update = makeRankingUpdateLocked(info); + updateUriPermissionsForActiveNotificationsLocked(info, true); } try { listener.onListenerConnected(update); @@ -9230,6 +9265,7 @@ public class NotificationManagerService extends SystemService { @Override @GuardedBy("mNotificationLock") protected void onServiceRemovedLocked(ManagedServiceInfo removed) { + updateUriPermissionsForActiveNotificationsLocked(removed, false); if (removeDisabledHints(removed)) { updateListenerHintsLocked(); updateEffectsSuppressorLocked(); @@ -9296,8 +9332,7 @@ public class NotificationManagerService extends SystemService { for (final ManagedServiceInfo info : getServices()) { boolean sbnVisible = isVisibleToListener(sbn, info); - boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) - : false; + boolean oldSbnVisible = (oldSbn != null) && isVisibleToListener(oldSbn, info); // This notification hasn't been and still isn't visible -> ignore. if (!oldSbnVisible && !sbnVisible) { continue; @@ -9321,13 +9356,8 @@ public class NotificationManagerService extends SystemService { // This notification became invisible -> remove the old one. if (oldSbnVisible && !sbnVisible) { final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight(); - mHandler.post(new Runnable() { - @Override - public void run() { - notifyRemoved( - info, oldSbnLightClone, update, null, REASON_USER_STOPPED); - } - }); + mHandler.post(() -> notifyRemoved( + info, oldSbnLightClone, update, null, REASON_USER_STOPPED)); continue; } @@ -9337,12 +9367,7 @@ public class NotificationManagerService extends SystemService { updateUriPermissions(r, old, info.component.getPackageName(), targetUserId); final StatusBarNotification sbnToPost = trimCache.ForListener(info); - mHandler.post(new Runnable() { - @Override - public void run() { - notifyPosted(info, sbnToPost, update); - } - }); + mHandler.post(() -> notifyPosted(info, sbnToPost, update)); } } catch (Exception e) { Slog.e(TAG, "Could not notify listeners for " + r.getKey(), e); @@ -9350,6 +9375,46 @@ public class NotificationManagerService extends SystemService { } /** + * Synchronously grant or revoke permissions to Uris for all active and visible + * notifications to just the NotificationListenerService provided. + */ + @GuardedBy("mNotificationLock") + private void updateUriPermissionsForActiveNotificationsLocked( + ManagedServiceInfo info, boolean grant) { + try { + for (final NotificationRecord r : mNotificationList) { + // When granting permissions, ignore notifications which are invisible. + // When revoking permissions, all notifications are invisible, so process all. + if (grant && !isVisibleToListener(r.getSbn(), info)) { + continue; + } + // If the notification is hidden, permissions are not required by the listener. + if (r.isHidden() && info.targetSdkVersion < Build.VERSION_CODES.P) { + continue; + } + // Grant or revoke access synchronously + final int targetUserId = (info.userid == UserHandle.USER_ALL) + ? UserHandle.USER_SYSTEM : info.userid; + if (grant) { + // Grant permissions by passing arguments as if the notification is new. + updateUriPermissions(/* newRecord */ r, /* oldRecord */ null, + info.component.getPackageName(), targetUserId); + } else { + // Revoke permissions by passing arguments as if the notification was + // removed, but set `onlyRevokeCurrentTarget` to avoid revoking permissions + // granted to *other* targets by this notification's URIs. + updateUriPermissions(/* newRecord */ null, /* oldRecord */ r, + info.component.getPackageName(), targetUserId, + /* onlyRevokeCurrentTarget */ true); + } + } + } catch (Exception e) { + Slog.e(TAG, "Could not " + (grant ? "grant" : "revoke") + " Uri permissions to " + + info.component, e); + } + } + + /** * asynchronously notify all listeners about a removed notification */ @GuardedBy("mNotificationLock") @@ -9384,18 +9449,11 @@ public class NotificationManagerService extends SystemService { final NotificationStats stats = mAssistants.isServiceTokenValidLocked(info.service) ? notificationStats : null; final NotificationRankingUpdate update = makeRankingUpdateLocked(info); - mHandler.post(new Runnable() { - @Override - public void run() { - notifyRemoved(info, sbnLight, update, stats, reason); - } - }); + mHandler.post(() -> notifyRemoved(info, sbnLight, update, stats, reason)); } // Revoke access after all listeners have been updated - mHandler.post(() -> { - updateUriPermissions(null, r, null, UserHandle.USER_SYSTEM); - }); + mHandler.post(() -> updateUriPermissions(null, r, null, UserHandle.USER_SYSTEM)); } /** diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index def9c78f98c7..3d7c978ca625 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -35,9 +35,6 @@ import android.content.pm.parsing.component.ParsedInstrumentation; import android.content.pm.parsing.component.ParsedIntentInfo; import android.content.pm.parsing.component.ParsedMainComponent; import android.content.pm.parsing.component.ParsedProvider; -import android.os.Handler; -import android.os.HandlerExecutor; -import android.os.HandlerThread; import android.os.Process; import android.os.Trace; import android.os.UserHandle; @@ -353,13 +350,9 @@ public class AppsFilter { injector.getUserManagerInternal().getUserInfos()); } }; - HandlerThread appsFilterThread = new HandlerThread("appsFilter"); - appsFilterThread.start(); - Handler appsFilterHandler = new Handler(appsFilterThread.getLooper()); - Executor executor = new HandlerExecutor(appsFilterHandler); - AppsFilter appsFilter = new AppsFilter(stateProvider, featureConfig, - forcedQueryablePackageNames, forceSystemAppsQueryable, null, executor); + forcedQueryablePackageNames, forceSystemAppsQueryable, null, + injector.getBackgroundExecutor()); featureConfig.setAppsFilter(appsFilter); return appsFilter; } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 672ad5e59428..cd383b9d1d7a 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -23,6 +23,8 @@ import android.annotation.UserIdInt; import android.content.Context; import android.content.pm.PackageStats; import android.os.Build; +import android.os.CreateAppDataArgs; +import android.os.CreateAppDataResult; import android.os.IBinder; import android.os.IBinder.DeathRecipient; import android.os.IInstalld; @@ -39,7 +41,10 @@ import dalvik.system.BlockGuard; import dalvik.system.VMRuntime; import java.io.FileDescriptor; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; public class Installer extends SystemService { private static final String TAG = "Installer"; @@ -176,37 +181,140 @@ public class Installer extends SystemService { } } + private static CreateAppDataArgs buildCreateAppDataArgs(String uuid, String packageName, + int userId, int flags, int appId, String seInfo, int targetSdkVersion) { + final CreateAppDataArgs args = new CreateAppDataArgs(); + args.uuid = uuid; + args.packageName = packageName; + args.userId = userId; + args.flags = flags; + args.appId = appId; + args.seInfo = seInfo; + args.targetSdkVersion = targetSdkVersion; + return args; + } + + private static CreateAppDataResult buildPlaceholderCreateAppDataResult() { + final CreateAppDataResult result = new CreateAppDataResult(); + result.ceDataInode = -1; + result.exceptionCode = 0; + result.exceptionMessage = null; + return result; + } + + /** + * @deprecated callers are encouraged to migrate to using {@link Batch} to + * more efficiently handle operations in bulk. + */ + @Deprecated public long createAppData(String uuid, String packageName, int userId, int flags, int appId, String seInfo, int targetSdkVersion) throws InstallerException { - if (!checkBeforeRemote()) return -1; + final CreateAppDataArgs args = buildCreateAppDataArgs(uuid, packageName, userId, flags, + appId, seInfo, targetSdkVersion); + final CreateAppDataResult result = createAppData(args); + if (result.exceptionCode == 0) { + return result.ceDataInode; + } else { + throw new InstallerException(result.exceptionMessage); + } + } + + public @NonNull CreateAppDataResult createAppData(@NonNull CreateAppDataArgs args) + throws InstallerException { + if (!checkBeforeRemote()) { + return buildPlaceholderCreateAppDataResult(); + } try { - return mInstalld.createAppData(uuid, packageName, userId, flags, appId, seInfo, - targetSdkVersion); + return mInstalld.createAppData(args); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + + public @NonNull CreateAppDataResult[] createAppDataBatched(@NonNull CreateAppDataArgs[] args) + throws InstallerException { + if (!checkBeforeRemote()) { + final CreateAppDataResult[] results = new CreateAppDataResult[args.length]; + Arrays.fill(results, buildPlaceholderCreateAppDataResult()); + return results; + } + try { + return mInstalld.createAppDataBatched(args); } catch (Exception e) { throw InstallerException.from(e); } } /** - * Batched version of createAppData for use with multiple packages. + * Class that collects multiple {@code installd} operations together in an + * attempt to more efficiently execute them in bulk. + * <p> + * Instead of returning results immediately, {@link CompletableFuture} + * instances are returned which can be used to chain follow-up work for each + * request. + * <p> + * The creator of this object <em>must</em> invoke {@link #execute()} + * exactly once to begin execution of all pending operations. Once execution + * has been kicked off, no additional events can be enqueued into this + * instance, but multiple instances can safely exist in parallel. */ - public void createAppDataBatched(String[] uuids, String[] packageNames, int userId, int flags, - int[] appIds, String[] seInfos, int[] targetSdkVersions) throws InstallerException { - if (!checkBeforeRemote()) return; - final int batchSize = 256; - for (int i = 0; i < uuids.length; i += batchSize) { - int to = i + batchSize; - if (to > uuids.length) { - to = uuids.length; - } - - try { - mInstalld.createAppDataBatched(Arrays.copyOfRange(uuids, i, to), - Arrays.copyOfRange(packageNames, i, to), userId, flags, - Arrays.copyOfRange(appIds, i, to), Arrays.copyOfRange(seInfos, i, to), - Arrays.copyOfRange(targetSdkVersions, i, to)); - } catch (Exception e) { - throw InstallerException.from(e); + public static class Batch { + private static final int CREATE_APP_DATA_BATCH_SIZE = 256; + + private boolean mExecuted; + + private final List<CreateAppDataArgs> mArgs = new ArrayList<>(); + private final List<CompletableFuture<Long>> mFutures = new ArrayList<>(); + + /** + * Enqueue the given {@code installd} operation to be executed in the + * future when {@link #execute(Installer)} is invoked. + * <p> + * Callers of this method are not required to hold a monitor lock on an + * {@link Installer} object. + */ + public synchronized @NonNull CompletableFuture<Long> createAppData(String uuid, + String packageName, int userId, int flags, int appId, String seInfo, + int targetSdkVersion) { + if (mExecuted) throw new IllegalStateException(); + + final CreateAppDataArgs args = buildCreateAppDataArgs(uuid, packageName, userId, flags, + appId, seInfo, targetSdkVersion); + final CompletableFuture<Long> future = new CompletableFuture<>(); + mArgs.add(args); + mFutures.add(future); + return future; + } + + /** + * Execute all pending {@code installd} operations that have been + * collected by this batch in a blocking fashion. + * <p> + * Callers of this method <em>must</em> hold a monitor lock on the given + * {@link Installer} object. + */ + public synchronized void execute(@NonNull Installer installer) throws InstallerException { + if (mExecuted) throw new IllegalStateException(); + mExecuted = true; + + final int size = mArgs.size(); + for (int i = 0; i < size; i += CREATE_APP_DATA_BATCH_SIZE) { + final CreateAppDataArgs[] args = new CreateAppDataArgs[Math.min(size - i, + CREATE_APP_DATA_BATCH_SIZE)]; + for (int j = 0; j < args.length; j++) { + args[j] = mArgs.get(i + j); + } + final CreateAppDataResult[] results = installer.createAppDataBatched(args); + for (int j = 0; j < args.length; j++) { + final CreateAppDataResult result = results[j]; + final CompletableFuture<Long> future = mFutures.get(i + j); + if (result.exceptionCode == 0) { + future.complete(result.ceDataInode); + } else { + future.completeExceptionally( + new InstallerException(result.exceptionMessage)); + } + } } } } diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java index 0b0f13929b2a..79f8dc1c9a1e 100644 --- a/services/core/java/com/android/server/pm/InstantAppResolver.java +++ b/services/core/java/com/android/server/pm/InstantAppResolver.java @@ -380,7 +380,7 @@ public abstract class InstantAppResolver { sanitizeIntent(request.origIntent), // This must only expose the secured version of the host request.hostDigestPrefixSecure, - UserHandle.getUserHandleForUid(request.userId), + UserHandle.of(request.userId), request.isRequesterInstantApp, request.token ); diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 0ce0f1e4d904..4b246c3b330c 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -613,6 +613,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements throw new IllegalArgumentException( "APEX files can only be installed as part of a staged session."); } + if (params.isMultiPackage) { + throw new IllegalArgumentException("A multi-session can't be set as APEX."); + } } if (params.isStaged && !isCalledBySystemOrShell(callingUid)) { diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index ff9edd511e84..28c5e964fe27 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -1328,12 +1328,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (PackageInstaller.STATUS_SUCCESS == status) { mChildSessionsRemaining.removeAt(sessionIndex); if (mChildSessionsRemaining.size() == 0) { - try { - intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, - PackageInstallerSession.this.sessionId); - mStatusReceiver.sendIntent(mContext, 0, intent, null, null); - } catch (IntentSender.SendIntentException ignore) { - } + destroyInternal(); + dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, + "Session installed", null); } } else if (PackageInstaller.STATUS_PENDING_USER_ACTION == status) { try { @@ -1492,53 +1489,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } /** - * Assert multipackage install has consistent sessions. - * - * @throws PackageManagerException if child sessions don't match parent session - * in respect to staged and enable rollback parameters. - */ - @GuardedBy("mLock") - private void assertMultiPackageConsistencyLocked( - @NonNull List<PackageInstallerSession> childSessions) throws PackageManagerException { - for (PackageInstallerSession childSession : childSessions) { - // It might be that the parent session is loaded before all of it's child sessions are, - // e.g. when reading sessions from XML. Those sessions will be null here, and their - // conformance with the multipackage params will be checked when they're loaded. - if (childSession == null) { - continue; - } - assertConsistencyWithLocked(childSession); - } - } - - /** - * Assert consistency with the given session. - * - * @throws PackageManagerException if other sessions doesn't match this session - * in respect to staged and enable rollback parameters. - */ - @GuardedBy("mLock") - private void assertConsistencyWithLocked(PackageInstallerSession other) - throws PackageManagerException { - // Session groups must be consistent wrt to isStaged parameter. Non-staging session - // cannot be grouped with staging sessions. - if (this.params.isStaged != other.params.isStaged) { - throw new PackageManagerException( - PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY, - "Multipackage Inconsistency: session " + other.sessionId - + " and session " + sessionId - + " have inconsistent staged settings"); - } - if (this.params.getEnableRollback() != other.params.getEnableRollback()) { - throw new PackageManagerException( - PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY, - "Multipackage Inconsistency: session " + other.sessionId - + " and session " + sessionId - + " have inconsistent rollback settings"); - } - } - - /** * Seal the session to prevent further modification. * * <p>The session will be sealed after calling this method even if it failed. @@ -1552,14 +1502,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { try { assertNoWriteFileTransfersOpenLocked(); assertPreparedAndNotDestroyedLocked("sealing of session"); - mSealed = true; - List<PackageInstallerSession> childSessions = getChildSessionsLocked(); - if (childSessions != null) { - assertMultiPackageConsistencyLocked(childSessions); - } - } catch (PackageManagerException e) { - throw onSessionValidationFailure(e); } catch (Throwable e) { // Convert all exceptions into package manager exceptions as only those are handled // in the code above. @@ -1637,7 +1580,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * If session should be sealed, then it's sealed to prevent further modification. * If the session can't be sealed then it's destroyed. * - * Additionally for staged APEX sessions read+validate the package and populate req'd fields. + * Additionally for staged APEX/APK sessions read+validate the package and populate req'd + * fields. * * <p> This is meant to be called after all of the sessions are loaded and added to * PackageInstallerService @@ -1670,6 +1614,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // APEX installations rely on certain fields to be populated after reboot. // E.g. mPackageName. validateApexInstallLocked(); + } else { + // Populate mPackageName for this APK session which is required by the staging + // manager to check duplicate apk-in-apex. + PackageInstallerSession parent = allSessions.get(mParentSessionId); + if (parent != null && parent.isStagedSessionReady()) { + validateApkInstallLocked(); + } } } catch (PackageManagerException e) { Slog.e(TAG, "Package not valid", e); @@ -2783,23 +2734,27 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - @Override - public void abandon() { - if (hasParentSessionId()) { - throw new IllegalStateException( - "Session " + sessionId + " is a child of multi-package session " - + getParentSessionId() + " and may not be abandoned directly."); + private void abandonNonStaged() { + synchronized (mLock) { + assertCallerIsOwnerOrRootLocked(); + if (mRelinquished) { + if (LOGD) Slog.d(TAG, "Ignoring abandon after commit relinquished control"); + return; + } + destroyInternal(); } + dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); + } + private void abandonStaged() { synchronized (mLock) { - if (params.isStaged && mDestroyed) { + if (mDestroyed) { // If a user abandons staged session in an unsafe state, then system will try to // abandon the destroyed staged session when it is safe on behalf of the user. assertCallerIsOwnerOrRootOrSystemLocked(); } else { assertCallerIsOwnerOrRootLocked(); } - if (isStagedAndInTerminalState()) { // We keep the session in the database if it's in a finalized state. It will be // removed by PackageInstallerService when the last update time is old enough. @@ -2807,26 +2762,35 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // do it now. return; } - if (mCommitted && params.isStaged) { - mDestroyed = true; + mDestroyed = true; + if (mCommitted) { if (!mStagingManager.abortCommittedSessionLocked(this)) { // Do not clean up the staged session from system. It is not safe yet. mCallback.onStagedSessionChanged(this); return; } - cleanStageDir(getChildSessionsLocked()); - } - - if (mRelinquished) { - Slog.d(TAG, "Ignoring abandon after commit relinquished control"); - return; } + cleanStageDir(getChildSessionsLocked()); destroyInternal(); } dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); } @Override + public void abandon() { + if (hasParentSessionId()) { + throw new IllegalStateException( + "Session " + sessionId + " is a child of multi-package session " + + getParentSessionId() + " and may not be abandoned directly."); + } + if (params.isStaged) { + abandonStaged(); + } else { + abandonNonStaged(); + } + } + + @Override public boolean isMultiPackage() { return params.isMultiPackage; } @@ -3185,6 +3149,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new IllegalStateException("Multi-session " + childSessionId + " can't be a child."); } + if (params.isStaged != childSession.params.isStaged) { + throw new IllegalStateException("Multipackage Inconsistency: session " + + childSession.sessionId + " and session " + sessionId + + " have inconsistent staged settings"); + } + if (params.getEnableRollback() != childSession.params.getEnableRollback()) { + throw new IllegalStateException("Multipackage Inconsistency: session " + + childSession.sessionId + " and session " + sessionId + + " have inconsistent rollback settings"); + } try { acquireTransactionLock(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 0d1c00dfe035..9c8b972985eb 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -248,6 +248,8 @@ import android.os.Debug; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; @@ -418,7 +420,9 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -923,6 +927,7 @@ public class PackageManagerService extends IPackageManager.Stub private final Object mLock; private final Installer mInstaller; private final Object mInstallLock; + private final Executor mBackgroundExecutor; // ----- producers ----- private final Singleton<ComponentResolver> mComponentResolverProducer; @@ -944,6 +949,7 @@ public class PackageManagerService extends IPackageManager.Stub Injector(Context context, Object lock, Installer installer, Object installLock, PackageAbiHelper abiHelper, + Executor backgroundExecutor, Producer<ComponentResolver> componentResolverProducer, Producer<PermissionManagerServiceInternal> permissionManagerProducer, Producer<UserManagerService> userManagerProducer, @@ -965,6 +971,7 @@ public class PackageManagerService extends IPackageManager.Stub mInstaller = installer; mAbiHelper = abiHelper; mInstallLock = installLock; + mBackgroundExecutor = backgroundExecutor; mComponentResolverProducer = new Singleton<>(componentResolverProducer); mPermissionManagerProducer = new Singleton<>(permissionManagerProducer); mUserManagerProducer = new Singleton<>(userManagerProducer); @@ -1078,6 +1085,10 @@ public class PackageManagerService extends IPackageManager.Stub public PlatformCompat getCompatibility() { return mPlatformCompatProducer.get(this, mPackageManager); } + + public Executor getBackgroundExecutor() { + return mBackgroundExecutor; + } } @VisibleForTesting(visibility = Visibility.PRIVATE) @@ -2580,17 +2591,21 @@ public class PackageManagerService extends IPackageManager.Stub t.traceBegin("create package manager"); final Object lock = new Object(); final Object installLock = new Object(); + HandlerThread backgroundThread = new HandlerThread("PackageManagerBg"); + backgroundThread.start(); + Handler backgroundHandler = new Handler(backgroundThread.getLooper()); Injector injector = new Injector( context, lock, installer, installLock, new PackageAbiHelperImpl(), + new HandlerExecutor(backgroundHandler), (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock), (i, pm) -> - PermissionManagerService.create(context, lock), + PermissionManagerService.create(context, lock), (i, pm) -> - new UserManagerService(context, pm, - new UserDataPreparer(installer, installLock, context, onlyCore), - lock), + new UserManagerService(context, pm, + new UserDataPreparer(installer, installLock, context, onlyCore), + lock), (i, pm) -> new Settings(Environment.getDataDirectory(), i.getPermissionManagerServiceInternal().getPermissionSettings(), @@ -3481,6 +3496,7 @@ public class PackageManagerService extends IPackageManager.Stub return; } int count = 0; + final Installer.Batch batch = new Installer.Batch(); for (String pkgName : deferPackages) { AndroidPackage pkg = null; synchronized (mLock) { @@ -3490,13 +3506,14 @@ public class PackageManagerService extends IPackageManager.Stub } } if (pkg != null) { - synchronized (mInstallLock) { - prepareAppDataAndMigrateLIF(pkg, UserHandle.USER_SYSTEM, storageFlags, - true /* maybeMigrateAppData */); - } + prepareAppDataAndMigrate(batch, pkg, UserHandle.USER_SYSTEM, storageFlags, + true /* maybeMigrateAppData */); count++; } } + synchronized (mInstallLock) { + executeBatchLI(batch); + } traceLog.traceEnd(); Slog.i(TAG, "Deferred reconcileAppsData finished " + count + " packages"); }, "prepareAppData"); @@ -7819,7 +7836,7 @@ public class PackageManagerService extends IPackageManager.Stub // low 'int'-sized word: relative priority among 'always' results. private long getDomainVerificationStatusLPr(PackageSetting ps, int userId) { long result = ps.getDomainVerificationStatusForUser(userId); - // if none available, get the master status + // if none available, get the status if (result >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) { if (ps.getIntentFilterVerificationInfo() != null) { result = ((long)ps.getIntentFilterVerificationInfo().getStatus()) << 32; @@ -17279,7 +17296,7 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile); - // Sanity check + // Validity check if (instantApp && onExternal) { Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal); throw new PrepareFailure(PackageManager.INSTALL_FAILED_SESSION_INVALID); @@ -17423,7 +17440,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - // Quick sanity check that we're signed correctly if updating; + // Quick validity check that we're signed correctly if updating; // we'll check this again later when scanning, but we want to // bail early here before tripping over redefined permissions. final KeySetManagerService ksms = mSettings.mKeySetManagerService; @@ -17591,9 +17608,6 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { pkgSetting = mSettings.getPackageLPr(pkgName); } - String abiOverride = - (pkgSetting == null || TextUtils.isEmpty(pkgSetting.cpuAbiOverrideString) - ? args.abiOverride : pkgSetting.cpuAbiOverrideString); boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null && pkgSetting.getPkgState().isUpdatedSystemApp(); AndroidPackage oldPackage = mPackages.get(pkgName); @@ -17601,7 +17615,7 @@ public class PackageManagerService extends IPackageManager.Stub final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi = mInjector.getAbiHelper().derivePackageAbi(parsedPackage, isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred, - abiOverride); + args.abiOverride); derivedAbi.first.applyTo(parsedPackage); derivedAbi.second.applyTo(parsedPackage); } catch (PackageManagerException pme) { @@ -21561,7 +21575,7 @@ public class PackageManagerService extends IPackageManager.Stub // had been set as a preferred activity. We try to clean this up // the next time we encounter that preferred activity, but it is // possible for the user flow to never be able to return to that - // situation so here we do a sanity check to make sure we haven't + // situation so here we do a validity check to make sure we haven't // left any junk around. ArrayList<PreferredActivity> removed = new ArrayList<>(); for (int i=0; i<mSettings.mPreferredActivities.size(); i++) { @@ -22746,6 +22760,14 @@ public class PackageManagerService extends IPackageManager.Stub } } + private void executeBatchLI(@NonNull Installer.Batch batch) { + try { + batch.execute(mInstaller); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to execute pending operations", e); + } + } + /** * Examine all apps present on given mounted volume, and destroy apps that * aren't expected, either due to uninstallation or reinstallation on @@ -22885,6 +22907,8 @@ public class PackageManagerService extends IPackageManager.Stub // Ensure that data directories are ready to roll for all packages // installed for this volume and user + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "prepareAppDataAndMigrate"); + Installer.Batch batch = new Installer.Batch(); final List<PackageSetting> packages; synchronized (mLock) { packages = mSettings.getVolumePackagesLPr(volumeUuid); @@ -22905,10 +22929,12 @@ public class PackageManagerService extends IPackageManager.Stub } if (ps.getInstalled(userId)) { - prepareAppDataAndMigrateLIF(ps.pkg, userId, flags, migrateAppData); + prepareAppDataAndMigrate(batch, ps.pkg, userId, flags, migrateAppData); preparedCount++; } } + executeBatchLI(batch); + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); Slog.v(TAG, "reconcileAppsData finished " + preparedCount + " packages"); return result; @@ -22933,6 +22959,7 @@ public class PackageManagerService extends IPackageManager.Stub mSettings.writeKernelMappingLPr(ps); } + Installer.Batch batch = new Installer.Batch(); UserManagerInternal umInternal = mInjector.getUserManagerInternal(); StorageManagerInternal smInternal = mInjector.getStorageManagerInternal(); for (UserInfo user : mUserManager.getUsers(false /*excludeDying*/)) { @@ -22947,16 +22974,20 @@ public class PackageManagerService extends IPackageManager.Stub if (ps.getInstalled(user.id)) { // TODO: when user data is locked, mark that we're still dirty - prepareAppDataLIF(pkg, user.id, flags); - - if (umInternal.isUserUnlockingOrUnlocked(user.id)) { - // Prepare app data on external storage; currently this is used to - // setup any OBB dirs that were created by the installer correctly. - int uid = UserHandle.getUid(user.id, UserHandle.getAppId(pkg.getUid())); - smInternal.prepareAppDataAfterInstall(pkg.getPackageName(), uid); - } + prepareAppData(batch, pkg, user.id, flags).thenRun(() -> { + // Note: this code block is executed with the Installer lock + // already held, since it's invoked as a side-effect of + // executeBatchLI() + if (umInternal.isUserUnlockingOrUnlocked(user.id)) { + // Prepare app data on external storage; currently this is used to + // setup any OBB dirs that were created by the installer correctly. + int uid = UserHandle.getUid(user.id, UserHandle.getAppId(pkg.getUid())); + smInternal.prepareAppDataAfterInstall(pkg.getPackageName(), uid); + } + }); } } + executeBatchLI(batch); } /** @@ -22967,26 +22998,33 @@ public class PackageManagerService extends IPackageManager.Stub * will try recovering system apps by wiping data; third-party app data is * left intact. */ - private void prepareAppDataLIF(AndroidPackage pkg, int userId, int flags) { + private @NonNull CompletableFuture<?> prepareAppData(@NonNull Installer.Batch batch, + @Nullable AndroidPackage pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); - return; + return CompletableFuture.completedFuture(null); } - prepareAppDataLeafLIF(pkg, userId, flags); + return prepareAppDataLeaf(batch, pkg, userId, flags); } - private void prepareAppDataAndMigrateLIF(AndroidPackage pkg, int userId, int flags, - boolean maybeMigrateAppData) { - prepareAppDataLIF(pkg, userId, flags); - - if (maybeMigrateAppData && maybeMigrateAppDataLIF(pkg, userId)) { - // We may have just shuffled around app data directories, so - // prepare them one more time - prepareAppDataLIF(pkg, userId, flags); - } + private @NonNull CompletableFuture<?> prepareAppDataAndMigrate(@NonNull Installer.Batch batch, + @NonNull AndroidPackage pkg, int userId, int flags, boolean maybeMigrateAppData) { + return prepareAppData(batch, pkg, userId, flags).thenRun(() -> { + // Note: this code block is executed with the Installer lock + // already held, since it's invoked as a side-effect of + // executeBatchLI() + if (maybeMigrateAppData && maybeMigrateAppDataLIF(pkg, userId)) { + // We may have just shuffled around app data directories, so + // prepare them one more time + final Installer.Batch batchInner = new Installer.Batch(); + prepareAppData(batchInner, pkg, userId, flags); + executeBatchLI(batchInner); + } + }); } - private void prepareAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) { + private @NonNull CompletableFuture<?> prepareAppDataLeaf(@NonNull Installer.Batch batch, + @NonNull AndroidPackage pkg, int userId, int flags) { if (DEBUG_APP_DATA) { Slog.v(TAG, "prepareAppData for " + pkg.getPackageName() + " u" + userId + " 0x" + Integer.toHexString(flags)); @@ -23006,57 +23044,67 @@ public class PackageManagerService extends IPackageManager.Stub Preconditions.checkNotNull(pkgSeInfo); final String seInfo = pkgSeInfo + (pkg.getSeInfoUser() != null ? pkg.getSeInfoUser() : ""); - long ceDataInode = -1; - try { - ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags, - appId, seInfo, pkg.getTargetSdkVersion()); - } catch (InstallerException e) { - if (pkg.isSystem()) { - logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName - + ", but trying to recover: " + e); - destroyAppDataLeafLIF(pkg, userId, flags); - try { - ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags, - appId, seInfo, pkg.getTargetSdkVersion()); - logCriticalInfo(Log.DEBUG, "Recovery succeeded!"); - } catch (InstallerException e2) { - logCriticalInfo(Log.DEBUG, "Recovery failed!"); - } - } else { - Slog.e(TAG, "Failed to create app data for " + packageName + ": " + e); - } - } - // Prepare the application profiles only for upgrades and first boot (so that we don't - // repeat the same operation at each boot). - // We only have to cover the upgrade and first boot here because for app installs we - // prepare the profiles before invoking dexopt (in installPackageLI). - // - // We also have to cover non system users because we do not call the usual install package - // methods for them. - // - // NOTE: in order to speed up first boot time we only create the current profile and do not - // update the content of the reference profile. A system image should already be configured - // with the right profile keys and the profiles for the speed-profile prebuilds should - // already be copied. That's done in #performDexOptUpgrade. - // - // TODO(calin, mathieuc): We should use .dm files for prebuilds profiles instead of - // manually copying them in #performDexOptUpgrade. When we do that we should have a more - // granular check here and only update the existing profiles. - if (mIsUpgrade || mFirstBoot || (userId != UserHandle.USER_SYSTEM)) { - mArtManagerService.prepareAppProfiles(pkg, userId, - /* updateReferenceProfileContent= */ false); - } + final int targetSdkVersion = pkg.getTargetSdkVersion(); + + return batch.createAppData(volumeUuid, packageName, userId, flags, appId, seInfo, + targetSdkVersion).whenComplete((ceDataInode, e) -> { + // Note: this code block is executed with the Installer lock + // already held, since it's invoked as a side-effect of + // executeBatchLI() + if ((e != null) && pkg.isSystem()) { + logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName + + ", but trying to recover: " + e); + destroyAppDataLeafLIF(pkg, userId, flags); + try { + ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, + flags, appId, seInfo, pkg.getTargetSdkVersion()); + logCriticalInfo(Log.DEBUG, "Recovery succeeded!"); + } catch (InstallerException e2) { + logCriticalInfo(Log.DEBUG, "Recovery failed!"); + } + } else if (e != null) { + Slog.e(TAG, "Failed to create app data for " + packageName + ": " + e); + } - if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) { - // TODO: mark this structure as dirty so we persist it! - synchronized (mLock) { - if (ps != null) { - ps.setCeDataInode(ceDataInode, userId); - } - } - } + // Prepare the application profiles only for upgrades and + // first boot (so that we don't repeat the same operation at + // each boot). + // + // We only have to cover the upgrade and first boot here + // because for app installs we prepare the profiles before + // invoking dexopt (in installPackageLI). + // + // We also have to cover non system users because we do not + // call the usual install package methods for them. + // + // NOTE: in order to speed up first boot time we only create + // the current profile and do not update the content of the + // reference profile. A system image should already be + // configured with the right profile keys and the profiles + // for the speed-profile prebuilds should already be copied. + // That's done in #performDexOptUpgrade. + // + // TODO(calin, mathieuc): We should use .dm files for + // prebuilds profiles instead of manually copying them in + // #performDexOptUpgrade. When we do that we should have a + // more granular check here and only update the existing + // profiles. + if (mIsUpgrade || mFirstBoot || (userId != UserHandle.USER_SYSTEM)) { + mArtManagerService.prepareAppProfiles(pkg, userId, + /* updateReferenceProfileContent= */ false); + } + + if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) { + // TODO: mark this structure as dirty so we persist it! + synchronized (mLock) { + if (ps != null) { + ps.setCeDataInode(ceDataInode, userId); + } + } + } - prepareAppDataContentsLeafLIF(pkg, ps, userId, flags); + prepareAppDataContentsLeafLIF(pkg, ps, userId, flags); + }); } private void prepareAppDataContentsLIF(AndroidPackage pkg, @Nullable PackageSetting pkgSetting, diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 7aeec6d68d26..32ec052a1fe0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -1043,7 +1043,9 @@ class PackageManagerShellCommand extends ShellCommand { + "; isStaged = " + session.isStaged() + "; isReady = " + session.isStagedSessionReady() + "; isApplied = " + session.isStagedSessionApplied() - + "; isFailed = " + session.isStagedSessionFailed() + ";"); + + "; isFailed = " + session.isStagedSessionFailed() + + "; errorMsg = " + session.getStagedSessionErrorMessage() + + ";"); } private Intent parseIntentAndUser() throws URISyntaxException { @@ -3338,7 +3340,7 @@ class PackageManagerShellCommand extends ShellCommand { session = new PackageInstaller.Session( mInterface.getPackageInstaller().openSession(sessionId)); if (!session.isMultiPackage() && !session.isStaged()) { - // Sanity check that all .dm files match an apk. + // Validity check that all .dm files match an apk. // (The installer does not support standalone .dm files and will not process them.) try { DexMetadataHelper.validateDexPaths(session.getNames()); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 3e3e3c590491..acb149b9ec3d 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -102,6 +102,7 @@ import com.android.internal.util.XmlUtils; import com.android.permission.persistence.RuntimePermissionsPersistence; import com.android.permission.persistence.RuntimePermissionsState; import com.android.server.LocalServices; +import com.android.server.pm.Installer.Batch; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; @@ -4148,24 +4149,12 @@ public final class Settings { final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", Trace.TRACE_TAG_PACKAGE_MANAGER); t.traceBegin("createNewUser-" + userHandle); - String[] volumeUuids; - String[] names; - int[] appIds; - String[] seinfos; - int[] targetSdkVersions; - int packagesCount; + Installer.Batch batch = new Installer.Batch(); final boolean skipPackageWhitelist = userTypeInstallablePackages == null; synchronized (mLock) { - Collection<PackageSetting> packages = mPackages.values(); - packagesCount = packages.size(); - volumeUuids = new String[packagesCount]; - names = new String[packagesCount]; - appIds = new int[packagesCount]; - seinfos = new String[packagesCount]; - targetSdkVersions = new int[packagesCount]; - Iterator<PackageSetting> packagesIterator = packages.iterator(); - for (int i = 0; i < packagesCount; i++) { - PackageSetting ps = packagesIterator.next(); + final int size = mPackages.size(); + for (int i = 0; i < size; i++) { + final PackageSetting ps = mPackages.valueAt(i); if (ps.pkg == null) { continue; } @@ -4187,18 +4176,15 @@ public final class Settings { } // Need to create a data directory for all apps under this user. Accumulate all // required args and call the installer after mPackages lock has been released - volumeUuids[i] = ps.volumeUuid; - names[i] = ps.name; - appIds[i] = ps.appId; - seinfos[i] = AndroidPackageUtils.getSeInfo(ps.pkg, ps); - targetSdkVersions[i] = ps.pkg.getTargetSdkVersion(); + final String seInfo = AndroidPackageUtils.getSeInfo(ps.pkg, ps); + batch.createAppData(ps.volumeUuid, ps.name, userHandle, + StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE, ps.appId, + seInfo, ps.pkg.getTargetSdkVersion()); } } t.traceBegin("createAppData"); - final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE; try { - installer.createAppDataBatched(volumeUuids, names, userHandle, flags, appIds, seinfos, - targetSdkVersions); + batch.execute(installer); } catch (InstallerException e) { Slog.w(TAG, "Failed to prepare app data", e); } @@ -4647,6 +4633,9 @@ public final class Settings { pw.print(prefix); pw.print(" resourcePath="); pw.println(ps.getCodePathString()); pw.print(prefix); pw.print(" legacyNativeLibraryDir="); pw.println(ps.legacyNativeLibraryPathString); + pw.print(prefix); pw.print(" extractNativeLibs="); + pw.println((ps.pkgFlags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0 + ? "true" : "false"); pw.print(prefix); pw.print(" primaryCpuAbi="); pw.println(ps.primaryCpuAbiString); pw.print(prefix); pw.print(" secondaryCpuAbi="); pw.println(ps.secondaryCpuAbiString); } diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index f9bf54a11df0..6d80f08ecbf6 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -56,6 +56,7 @@ import android.os.UserManagerInternal; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; import android.text.TextUtils; +import android.util.ArraySet; import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; @@ -84,6 +85,7 @@ import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -589,13 +591,14 @@ public class StagingManager { // If checkpoint is supported, then we only resume sessions if we are in checkpointing // mode. If not, we fail all sessions. if (supportsCheckpoint() && !needsCheckpoint()) { - String errorMsg = "Reverting back to safe state. Marking " + session.sessionId - + " as failed"; - if (!TextUtils.isEmpty(mFailureReason)) { - errorMsg = errorMsg + ": " + mFailureReason; + String revertMsg = "Reverting back to safe state. Marking " + + session.sessionId + " as failed."; + final String reasonForRevert = getReasonForRevert(); + if (!TextUtils.isEmpty(reasonForRevert)) { + revertMsg += " Reason for revert: " + reasonForRevert; } - Slog.d(TAG, errorMsg); - session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, errorMsg); + Slog.d(TAG, revertMsg); + session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, revertMsg); return; } } catch (RemoteException e) { @@ -650,6 +653,7 @@ public class StagingManager { try { if (hasApex) { checkInstallationOfApkInApexSuccessful(session); + checkDuplicateApkInApex(session); snapshotAndRestoreForApexSession(session); Slog.i(TAG, "APEX packages in session " + session.sessionId + " were successfully activated. Proceeding with APK packages, if any"); @@ -699,6 +703,16 @@ public class StagingManager { } } + private String getReasonForRevert() { + if (!TextUtils.isEmpty(mFailureReason)) { + return mFailureReason; + } + if (!TextUtils.isEmpty(mNativeFailureReason)) { + return "Session reverted due to crashing native process: " + mNativeFailureReason; + } + return ""; + } + private List<String> findAPKsInDir(File stageDir) { List<String> ret = new ArrayList<>(); if (stageDir != null && stageDir.exists()) { @@ -829,6 +843,40 @@ public class StagingManager { return null; } + /** + * Throws a PackageManagerException if there are duplicate packages in apk and apk-in-apex. + */ + private void checkDuplicateApkInApex(@NonNull PackageInstallerSession session) + throws PackageManagerException { + if (!session.isMultiPackage()) { + return; + } + final int[] childSessionIds = session.getChildSessionIds(); + final Set<String> apkNames = new ArraySet<>(); + synchronized (mStagedSessions) { + for (int id : childSessionIds) { + final PackageInstallerSession s = mStagedSessions.get(id); + if (!isApexSession(s)) { + apkNames.add(s.getPackageName()); + } + } + } + final List<PackageInstallerSession> apexSessions = extractApexSessions(session); + for (PackageInstallerSession apexSession : apexSessions) { + String packageName = apexSession.getPackageName(); + for (String apkInApex : mApexManager.getApksInApex(packageName)) { + if (!apkNames.add(apkInApex)) { + throw new PackageManagerException( + SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, + "Package: " + packageName + " in session: " + + apexSession.sessionId + " has duplicate apk-in-apex: " + + apkInApex, null); + + } + } + } + } + private void installApksInSession(@NonNull PackageInstallerSession session) throws PackageManagerException { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 8b2ba9c98f23..d137fd05f793 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -87,6 +87,7 @@ import android.stats.devicepolicy.DevicePolicyEnums; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.IndentingPrintWriter; import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; @@ -103,7 +104,6 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.FrameworkStatsLog; -import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.widget.LockPatternUtils; @@ -141,6 +141,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicInteger; /** * Service for {@link UserManager}. @@ -443,6 +444,11 @@ public class UserManagerService extends IUserManager.Stub { } }; + // TODO(b/161915546): remove once userWithName() is fixed / removed + // Use to debug / dump when user 0 is allocated at userWithName() + public static final boolean DBG_ALLOCATION = false; // DO NOT SUBMIT WITH TRUE + public final AtomicInteger mUser0Allocations; + /** * Start an {@link IntentSender} when user is unlocked after disabling quiet mode. * @@ -493,20 +499,23 @@ public class UserManagerService extends IUserManager.Stub { states = new SparseIntArray(); invalidateIsUserUnlockedCache(); } - public int get(int userId) { + public int get(@UserIdInt int userId) { return states.get(userId); } - public int get(int userId, int fallback) { + public int get(@UserIdInt int userId, int fallback) { return states.indexOfKey(userId) >= 0 ? states.get(userId) : fallback; } - public void put(int userId, int state) { + public void put(@UserIdInt int userId, int state) { states.put(userId, state); invalidateIsUserUnlockedCache(); } - public void delete(int userId) { + public void delete(@UserIdInt int userId) { states.delete(userId); invalidateIsUserUnlockedCache(); } + public boolean has(@UserIdInt int userId) { + return states.get(userId, UserHandle.USER_NULL) != UserHandle.USER_NULL; + } @Override public String toString() { return states.toString(); @@ -629,6 +638,7 @@ public class UserManagerService extends IUserManager.Stub { LocalServices.addService(UserManagerInternal.class, mLocalService); mLockPatternUtils = new LockPatternUtils(mContext); mUserStates.put(UserHandle.USER_SYSTEM, UserState.STATE_BOOTING); + mUser0Allocations = DBG_ALLOCATION ? new AtomicInteger() : null; } void systemReady() { @@ -1310,6 +1320,10 @@ public class UserManagerService extends IUserManager.Stub { */ private UserInfo userWithName(UserInfo orig) { if (orig != null && orig.name == null && orig.id == UserHandle.USER_SYSTEM) { + if (DBG_ALLOCATION) { + final int number = mUser0Allocations.incrementAndGet(); + Slog.w(LOG_TAG, "System user instantiated at least " + number + " times"); + } UserInfo withName = new UserInfo(orig); withName.name = getOwnerName(); return withName; @@ -3550,6 +3564,13 @@ public class UserManagerService extends IUserManager.Stub { if (preCreatedUserData == null) { return null; } + synchronized (mUserStates) { + if (mUserStates.has(preCreatedUserData.info.id)) { + Slog.w(LOG_TAG, "Cannot reuse pre-created user " + + preCreatedUserData.info.id + " because it didn't stop yet"); + return null; + } + } final UserInfo preCreatedUser = preCreatedUserData.info; final int newFlags = preCreatedUser.flags | flags; if (!checkUserTypeConsistency(newFlags)) { @@ -4833,6 +4854,10 @@ public class UserManagerService extends IUserManager.Stub { pw.println(" Is split-system user: " + UserManager.isSplitSystemUser()); pw.println(" Is headless-system mode: " + UserManager.isHeadlessSystemUserMode()); pw.println(" User version: " + mUserVersion); + pw.println(" Owner name: " + getOwnerName()); + if (DBG_ALLOCATION) { + pw.println(" System user allocations: " + mUser0Allocations.get()); + } // Dump UserTypes pw.println(); @@ -4842,9 +4867,17 @@ public class UserManagerService extends IUserManager.Stub { mUserTypes.valueAt(i).dump(pw, " "); } - // Dump package whitelist - pw.println(); - mSystemPackageInstaller.dump(pw); + // TODO: create IndentingPrintWriter at the beginning of dump() and use the proper + // indentation methods instead of explicit printing " " + try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw)) { + + // Dump SystemPackageInstaller info + ipw.println(); + mSystemPackageInstaller.dump(ipw); + + // NOTE: pw's not available after this point as it's auto-closed by ipw, so new dump + // statements should use ipw below + } } private static void dumpTimeAgo(PrintWriter pw, StringBuilder sb, long nowTime, long time) { @@ -5202,6 +5235,7 @@ public class UserManagerService extends IUserManager.Stub { return userData == null ? null : userData.info; } + @Override public @NonNull UserInfo[] getUserInfos() { synchronized (mUsersLock) { int userSize = mUsers.size(); diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java index 492b84a0a84b..b95404febf72 100644 --- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java +++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java @@ -28,15 +28,14 @@ import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.DebugUtils; +import android.util.IndentingPrintWriter; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.SystemConfig; import com.android.server.pm.parsing.pkg.AndroidPackage; -import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -723,13 +722,7 @@ class UserSystemPackageInstaller { return userTypeList; } - void dump(PrintWriter pw) { - try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) { - dumpIndented(ipw); - } - } - - private void dumpIndented(IndentingPrintWriter pw) { + void dump(IndentingPrintWriter pw) { final int mode = getWhitelistMode(); pw.println("Whitelisted packages per user type"); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 03771be0ce7c..1be74154b53a 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -83,6 +83,7 @@ import android.content.pm.PackageParser; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; +import android.content.pm.UserInfo; import android.content.pm.parsing.component.ParsedPermission; import android.content.pm.parsing.component.ParsedPermissionGroup; import android.content.pm.permission.SplitPermissionInfoParcelable; @@ -120,6 +121,7 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.TimingsTraceLog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -879,7 +881,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public int checkPermission(String permName, String pkgName, int userId) { + public int checkPermission(String permName, String pkgName, @UserIdInt int userId) { // Not using Objects.requireNonNull() here for compatibility reasons. if (permName == null || pkgName == null) { return PackageManager.PERMISSION_DENIED; @@ -2462,7 +2464,28 @@ public class PermissionManagerService extends IPermissionManager.Stub { return null; } final PermissionsState permissionsState = ps.getPermissionsState(); - return permissionsState.getPermissions(userId); + if (!ps.getInstantApp(userId)) { + return permissionsState.getPermissions(userId); + } else { + // Install permission state is shared among all users, but instant app state is + // per-user, so we can only filter it here unless we make install permission state + // per-user as well. + final Set<String> instantPermissions = new ArraySet<>(permissionsState.getPermissions( + userId)); + instantPermissions.removeIf(permissionName -> { + BasePermission permission = mSettings.getPermission(permissionName); + if (permission == null) { + return true; + } + if (!permission.isInstant()) { + EventLog.writeEvent(0x534e4554, "140256621", UserHandle.getUid(userId, + ps.getAppId()), permissionName); + return true; + } + return false; + }); + return instantPermissions; + } } @Nullable @@ -2521,12 +2544,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { final PermissionsState permissionsState = ps.getPermissionsState(); - final int[] currentUserIds = UserManagerService.getInstance().getUserIds(); + final int[] userIds = getAllUserIds(); boolean runtimePermissionsRevoked = false; int[] updatedUserIds = EMPTY_INT_ARRAY; - for (int userId : currentUserIds) { + for (int userId : userIds) { if (permissionsState.isMissing(userId)) { Collection<String> requestedPermissions; int targetSdkVersion; @@ -2592,7 +2615,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { // runtime and revocation of a runtime from a shared user. synchronized (mLock) { updatedUserIds = revokeUnusedSharedUserPermissionsLocked(ps.getSharedUser(), - currentUserIds); + userIds); if (!ArrayUtils.isEmpty(updatedUserIds)) { runtimePermissionsRevoked = true; } @@ -2747,7 +2770,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { // a runtime permission being downgraded to an install one. // Also in permission review mode we keep dangerous permissions // for legacy apps - for (int userId : currentUserIds) { + for (int userId : userIds) { if (origPermissions.getRuntimePermissionState( perm, userId) != null) { // Revoke the runtime permission and clear the flags. @@ -2770,7 +2793,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { boolean hardRestricted = bp.isHardRestricted(); boolean softRestricted = bp.isSoftRestricted(); - for (int userId : currentUserIds) { + for (int userId : userIds) { // If permission policy is not ready we don't deal with restricted // permissions as the policy may whitelist some permissions. Once // the policy is initialized we would re-evaluate permissions. @@ -2909,7 +2932,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { boolean hardRestricted = bp.isHardRestricted(); boolean softRestricted = bp.isSoftRestricted(); - for (int userId : currentUserIds) { + for (int userId : userIds) { // If permission policy is not ready we don't deal with restricted // permissions as the policy may whitelist some permissions. Once // the policy is initialized we would re-evaluate permissions. @@ -3061,10 +3084,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { synchronized (mLock) { updatedUserIds = revokePermissionsNoLongerImplicitLocked(permissionsState, pkg, - currentUserIds, updatedUserIds); + userIds, updatedUserIds); updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origPermissions, - permissionsState, pkg, newImplicitPermissions, currentUserIds, updatedUserIds); - updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, currentUserIds, + permissionsState, pkg, newImplicitPermissions, userIds, updatedUserIds); + updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, userIds, updatedUserIds); } @@ -3081,6 +3104,25 @@ public class PermissionManagerService extends IPermissionManager.Stub { } /** + * Returns all relevant user ids. This list include the current set of created user ids as well + * as pre-created user ids. + * @return user ids for created users and pre-created users + */ + private int[] getAllUserIds() { + final TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER); + t.traceBegin("getAllUserIds"); + List<UserInfo> users = UserManagerService.getInstance().getUsers( + /*excludePartial=*/ true, /*excludeDying=*/ true, /*excludePreCreated=*/ false); + int size = users.size(); + final int[] userIds = new int[size]; + for (int i = 0; i < size; i++) { + userIds[i] = users.get(i).id; + } + t.traceEnd(); + return userIds; + } + + /** * Revoke permissions that are not implicit anymore and that have * {@link PackageManager#FLAG_PERMISSION_REVOKE_WHEN_REQUESTED} set. * @@ -4406,7 +4448,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { * @param checkShell whether to prevent shell from access if there's a debugging restriction * @param message the message to log on security exception */ - private void enforceCrossUserPermission(int callingUid, int userId, + private void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, boolean requirePermissionWhenSameUser, String message) { if (userId < 0) { @@ -4423,7 +4465,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { return; } String errorMessage = buildInvalidCrossUserPermissionMessage( - message, requireFullPermission); + callingUid, userId, message, requireFullPermission); Slog.w(TAG, errorMessage); throw new SecurityException(errorMessage); } @@ -4442,7 +4484,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { * @param checkShell whether to prevent shell from access if there's a debugging restriction * @param message the message to log on security exception */ - private void enforceCrossUserOrProfilePermission(int callingUid, int userId, + private void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, String message) { if (userId < 0) { @@ -4468,7 +4510,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { return; } String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage( - message, requireFullPermission, isSameProfileGroup); + callingUid, userId, message, requireFullPermission, isSameProfileGroup); Slog.w(TAG, errorMessage); throw new SecurityException(errorMessage); } @@ -4503,44 +4545,48 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - private static String buildInvalidCrossUserPermissionMessage( - String message, boolean requireFullPermission) { + private static String buildInvalidCrossUserPermissionMessage(int callingUid, + @UserIdInt int userId, String message, boolean requireFullPermission) { StringBuilder builder = new StringBuilder(); if (message != null) { builder.append(message); builder.append(": "); } - builder.append("Requires "); + builder.append("UID "); + builder.append(callingUid); + builder.append(" requires "); builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - if (requireFullPermission) { - builder.append("."); - return builder.toString(); + if (!requireFullPermission) { + builder.append(" or "); + builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS); } - builder.append(" or "); - builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS); + builder.append(" to access user "); + builder.append(userId); builder.append("."); return builder.toString(); } - private static String buildInvalidCrossUserOrProfilePermissionMessage( - String message, boolean requireFullPermission, boolean isSameProfileGroup) { + private static String buildInvalidCrossUserOrProfilePermissionMessage(int callingUid, + @UserIdInt int userId, String message, boolean requireFullPermission, + boolean isSameProfileGroup) { StringBuilder builder = new StringBuilder(); if (message != null) { builder.append(message); builder.append(": "); } - builder.append("Requires "); + builder.append("UID "); + builder.append(callingUid); + builder.append(" requires "); builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - if (requireFullPermission) { - builder.append("."); - return builder.toString(); - } - builder.append(" or "); - builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS); - if (isSameProfileGroup) { + if (!requireFullPermission) { builder.append(" or "); - builder.append(android.Manifest.permission.INTERACT_ACROSS_PROFILES); + builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS); + if (isSameProfileGroup) { + builder.append(" or "); + builder.append(android.Manifest.permission.INTERACT_ACROSS_PROFILES); + } } + builder.append(" to access user "); builder.append("."); return builder.toString(); } diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index 4d48a2e63b30..ae2b040d0a89 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -89,7 +89,7 @@ import java.util.concurrent.ExecutionException; public final class PermissionPolicyService extends SystemService { private static final String LOG_TAG = PermissionPolicyService.class.getSimpleName(); private static final boolean DEBUG = false; - private static final long USER_SENSITIVE_UPDATE_DELAY_MS = 10000; + private static final long USER_SENSITIVE_UPDATE_DELAY_MS = 60000; private final Object mLock = new Object(); @@ -283,6 +283,11 @@ public final class PermissionPolicyService extends SystemService { manager.updateUserSensitiveForApp(uid); } }, UserHandle.ALL, intentFilter, null, null); + + PermissionControllerManager manager = new PermissionControllerManager( + getUserContext(getContext(), Process.myUserHandle()), FgThread.getHandler()); + FgThread.getHandler().postDelayed(manager::updateUserSensitive, + USER_SENSITIVE_UPDATE_DELAY_MS); } /** @@ -425,8 +430,7 @@ public final class PermissionPolicyService extends SystemService { throw new IllegalStateException(e); } - FgThread.getHandler().postDelayed(permissionControllerManager::updateUserSensitive, - USER_SENSITIVE_UPDATE_DELAY_MS); + permissionControllerManager.updateUserSensitive(); packageManagerInternal.updateRuntimePermissionsFingerprint(userId); } diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index ef4954aa4e4c..ae0db4419080 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -200,7 +200,7 @@ public class ThermalManagerService extends SystemService { final int count = mTemperatureMap.size(); for (int i = 0; i < count; i++) { Temperature t = mTemperatureMap.valueAt(i); - if (t.getStatus() >= newStatus) { + if (t.getType() == Temperature.TYPE_SKIN && t.getStatus() >= newStatus) { newStatus = t.getStatus(); } } diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java index 2a74b3d23829..5aedfc19028b 100644 --- a/services/core/java/com/android/server/slice/SliceManagerService.java +++ b/services/core/java/com/android/server/slice/SliceManagerService.java @@ -28,8 +28,6 @@ import static android.os.Process.SYSTEM_UID; import android.Manifest.permission; import android.annotation.NonNull; import android.app.AppOpsManager; -import android.app.role.OnRoleHoldersChangedListener; -import android.app.role.RoleManager; import android.app.slice.ISliceManager; import android.app.slice.SliceSpec; import android.app.usage.UsageStatsManagerInternal; @@ -41,9 +39,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; import android.content.pm.ProviderInfo; -import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Binder; import android.os.Handler; @@ -65,7 +61,6 @@ import com.android.internal.app.AssistUtils; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -77,10 +72,7 @@ import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; import java.io.IOException; import java.util.ArrayList; -import java.util.List; import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.function.Supplier; public class SliceManagerService extends ISliceManager.Stub { @@ -88,16 +80,13 @@ public class SliceManagerService extends ISliceManager.Stub { private final Object mLock = new Object(); private final Context mContext; - private final PackageManagerInternal mPackageManagerInternal; private final AppOpsManager mAppOps; private final AssistUtils mAssistUtils; @GuardedBy("mLock") private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>(); @GuardedBy("mLock") - private final SparseArray<PackageMatchingCache> mAssistantLookup = new SparseArray<>(); - @GuardedBy("mLock") - private final SparseArray<PackageMatchingCache> mHomeLookup = new SparseArray<>(); + private final SparseArray<String> mLastAssistantPackage = new SparseArray<>(); private final Handler mHandler; private final SlicePermissionManager mPermissions; @@ -110,8 +99,6 @@ public class SliceManagerService extends ISliceManager.Stub { @VisibleForTesting SliceManagerService(Context context, Looper looper) { mContext = context; - mPackageManagerInternal = Objects.requireNonNull( - LocalServices.getService(PackageManagerInternal.class)); mAppOps = context.getSystemService(AppOpsManager.class); mAssistUtils = new AssistUtils(context); mHandler = new Handler(looper); @@ -124,7 +111,6 @@ public class SliceManagerService extends ISliceManager.Stub { filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); - mRoleObserver = new RoleObserver(); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); } @@ -174,8 +160,7 @@ public class SliceManagerService extends ISliceManager.Stub { mHandler.post(() -> { if (slicePkg != null && !Objects.equals(pkg, slicePkg)) { mAppUsageStats.reportEvent(slicePkg, user, - isAssistant(pkg, user) || isDefaultHomeApp(pkg, user) - ? SLICE_PINNED_PRIV : SLICE_PINNED); + isAssistant(pkg, user) ? SLICE_PINNED_PRIV : SLICE_PINNED); } }); } @@ -440,38 +425,19 @@ public class SliceManagerService extends ISliceManager.Stub { private boolean hasFullSliceAccess(String pkg, int userId) { long ident = Binder.clearCallingIdentity(); try { - boolean ret = isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId) - || isGrantedFullAccess(pkg, userId); - return ret; + return isAssistant(pkg, userId) || isGrantedFullAccess(pkg, userId); } finally { Binder.restoreCallingIdentity(ident); } } private boolean isAssistant(String pkg, int userId) { - return getAssistantMatcher(userId).matches(pkg); - } - - private boolean isDefaultHomeApp(String pkg, int userId) { - return getHomeMatcher(userId).matches(pkg); - } - - private PackageMatchingCache getAssistantMatcher(int userId) { - PackageMatchingCache matcher = mAssistantLookup.get(userId); - if (matcher == null) { - matcher = new PackageMatchingCache(() -> getAssistant(userId)); - mAssistantLookup.put(userId, matcher); - } - return matcher; - } - - private PackageMatchingCache getHomeMatcher(int userId) { - PackageMatchingCache matcher = mHomeLookup.get(userId); - if (matcher == null) { - matcher = new PackageMatchingCache(() -> getDefaultHome(userId)); - mHomeLookup.put(userId, matcher); + if (pkg == null) return false; + if (!pkg.equals(mLastAssistantPackage.get(userId))) { + // Failed on cached value, try updating. + mLastAssistantPackage.put(userId, getAssistant(userId)); } - return matcher; + return pkg.equals(mLastAssistantPackage.get(userId)); } private String getAssistant(int userId) { @@ -482,111 +448,6 @@ public class SliceManagerService extends ISliceManager.Stub { return cn.getPackageName(); } - /** - * A cached value of the default home app - */ - private String mCachedDefaultHome = null; - - // Based on getDefaultHome in ShortcutService. - // TODO: Unify if possible - @VisibleForTesting - protected String getDefaultHome(int userId) { - - // Set VERIFY to true to run the cache in "shadow" mode for cache - // testing. Do not commit set to true; - final boolean VERIFY = false; - - if (mCachedDefaultHome != null) { - if (!VERIFY) { - return mCachedDefaultHome; - } - } - - final long token = Binder.clearCallingIdentity(); - try { - final List<ResolveInfo> allHomeCandidates = new ArrayList<>(); - - // Default launcher from package manager. - final ComponentName defaultLauncher = mPackageManagerInternal - .getHomeActivitiesAsUser(allHomeCandidates, userId); - - ComponentName detected = defaultLauncher; - - // Cache the default launcher. It is not a problem if the - // launcher is null - eventually, the default launcher will be - // set to something non-null. - mCachedDefaultHome = ((detected != null) ? detected.getPackageName() : null); - - if (detected == null) { - // If we reach here, that means it's the first check since the user was created, - // and there's already multiple launchers and there's no default set. - // Find the system one with the highest priority. - // (We need to check the priority too because of FallbackHome in Settings.) - // If there's no system launcher yet, then no one can access slices, until - // the user explicitly sets one. - final int size = allHomeCandidates.size(); - - int lastPriority = Integer.MIN_VALUE; - for (int i = 0; i < size; i++) { - final ResolveInfo ri = allHomeCandidates.get(i); - if (!ri.activityInfo.applicationInfo.isSystemApp()) { - continue; - } - if (ri.priority < lastPriority) { - continue; - } - detected = ri.activityInfo.getComponentName(); - lastPriority = ri.priority; - } - } - final String ret = ((detected != null) ? detected.getPackageName() : null); - if (VERIFY) { - if (mCachedDefaultHome != null && !mCachedDefaultHome.equals(ret)) { - Slog.e(TAG, "getDefaultHome() cache failure, is " + - mCachedDefaultHome + " should be " + ret); - } - } - return ret; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public void invalidateCachedDefaultHome() { - mCachedDefaultHome = null; - } - - /** - * Listen for changes in the roles, and invalidate the cached default - * home as necessary. - */ - private RoleObserver mRoleObserver; - - class RoleObserver implements OnRoleHoldersChangedListener { - private RoleManager mRm; - private final Executor mExecutor; - - RoleObserver() { - mExecutor = mContext.getMainExecutor(); - register(); - } - - public void register() { - mRm = mContext.getSystemService(RoleManager.class); - if (mRm != null) { - mRm.addOnRoleHoldersChangedListenerAsUser(mExecutor, this, UserHandle.ALL); - invalidateCachedDefaultHome(); - } - } - - @Override - public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) { - if (RoleManager.ROLE_HOME.equals(roleName)) { - invalidateCachedDefaultHome(); - } - } - } - private boolean isGrantedFullAccess(String pkg, int userId) { return mPermissions.hasFullAccess(pkg, userId); } @@ -635,30 +496,6 @@ public class SliceManagerService extends ISliceManager.Stub { return mPermissions.getAllPackagesGranted(pkg); } - /** - * Holder that caches a package that has access to a slice. - */ - static class PackageMatchingCache { - - private final Supplier<String> mPkgSource; - private String mCurrentPkg; - - public PackageMatchingCache(Supplier<String> pkgSource) { - mPkgSource = pkgSource; - } - - public boolean matches(String pkgCandidate) { - if (pkgCandidate == null) return false; - - if (Objects.equals(pkgCandidate, mCurrentPkg)) { - return true; - } - // Failed on cached value, try updating. - mCurrentPkg = mPkgSource.get(); - return Objects.equals(pkgCandidate, mCurrentPkg); - } - } - public static class Lifecycle extends SystemService { private SliceManagerService mService; diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java index c2b0d1067e38..0ca36e0fc258 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java @@ -72,6 +72,10 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat builder.setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED); } + // TODO(b/149014708) Replace this with real logic when the settings storage is fully + // implemented. + builder.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_SUPPORTED); + // The ability to make manual time zone suggestions can also be restricted by policy. With // the current logic above, this could lead to a situation where a device hardware does not // support auto detection, the device has been forced into "auto" mode by an admin and the @@ -90,6 +94,7 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat public TimeZoneConfiguration getConfiguration(@UserIdInt int userId) { return new TimeZoneConfiguration.Builder() .setAutoDetectionEnabled(isAutoDetectionEnabled()) + .setGeoDetectionEnabled(isGeoDetectionEnabled()) .build(); } @@ -105,8 +110,11 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat // detection: if we wrote it down then we'd set the default explicitly. That might influence // what happens on later releases that do support auto detection on the same hardware. if (isAutoDetectionSupported()) { - final int value = configuration.isAutoDetectionEnabled() ? 1 : 0; - Settings.Global.putInt(mCr, Settings.Global.AUTO_TIME_ZONE, value); + final int autoEnabledValue = configuration.isAutoDetectionEnabled() ? 1 : 0; + Settings.Global.putInt(mCr, Settings.Global.AUTO_TIME_ZONE, autoEnabledValue); + + final boolean geoTzDetectionEnabledValue = configuration.isGeoDetectionEnabled(); + // TODO(b/149014708) Write this down to user-scoped settings once implemented. } } @@ -126,6 +134,14 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat } @Override + public boolean isGeoDetectionEnabled() { + // TODO(b/149014708) Read this from user-scoped settings once implemented. The user's + // location toggle will act as an override for this setting, i.e. so that the setting will + // return false if the location toggle is disabled. + return false; + } + + @Override public boolean isDeviceTimeZoneInitialized() { // timezone.equals("GMT") will be true and only true if the time zone was // set to a default value by the system server (when starting, system server diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java index 3d9ec6475a8b..fb7a73d12632 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; * The internal (in-process) system server API for the {@link * com.android.server.timezonedetector.TimeZoneDetectorService}. * + * <p>The methods on this class can be called from any thread. * @hide */ public interface TimeZoneDetectorInternal extends Dumpable.Container { @@ -29,7 +30,7 @@ public interface TimeZoneDetectorInternal extends Dumpable.Container { /** * Suggests the current time zone, determined using geolocation, to the detector. The * detector may ignore the signal based on system settings, whether better information is - * available, and so on. + * available, and so on. This method may be implemented asynchronously. */ void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion); } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java index 4464f7d136e3..15412a0d14a1 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java @@ -58,6 +58,7 @@ public final class TimeZoneDetectorInternalImpl implements TimeZoneDetectorInter @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) { Objects.requireNonNull(timeZoneSuggestion); + // All strategy calls must take place on the mHandler thread. mHandler.post( () -> mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(timeZoneSuggestion)); } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java index 7467439905eb..d81f949742bd 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java @@ -130,6 +130,10 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub service.handleAutoTimeZoneConfigChanged(); } }); + // TODO(b/149014708) Listen for changes to geolocation time zone detection enabled config. + // This should also include listening to the current user and the current user's location + // toggle since the config is user-scoped and the location toggle overrides the geolocation + // time zone enabled setting. return service; } @@ -280,7 +284,7 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub void handleConfigurationChanged() { // Note: we could trigger an async time zone detection operation here via a call to - // handleAutoTimeZoneDetectionChanged(), but that is triggered in response to the underlying + // handleAutoTimeZoneConfigChanged(), but that is triggered in response to the underlying // setting value changing so it is currently unnecessary. If we get to a point where all // configuration changes are guaranteed to happen in response to an updateConfiguration() // call, then we can remove that path and call it here instead. @@ -288,7 +292,6 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub // Configuration has changed, but each user may have a different view of the configuration. // It's possible that this will cause unnecessary notifications but that shouldn't be a // problem. - synchronized (mConfigurationListeners) { final int userCount = mConfigurationListeners.size(); for (int userIndex = 0; userIndex < userCount; userIndex++) { diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java index f947a6554412..c5b7e39f4fef 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java @@ -30,9 +30,9 @@ import android.util.IndentingPrintWriter; * <p>The strategy uses suggestions to decide whether to modify the device's time zone setting * and what to set it to. * - * <p>Most calls will be handled by a single thread but that is not true for all calls. For example - * {@link #dump(IndentingPrintWriter, String[])}) may be called on a different thread so - * implementations mustvhandle thread safety. + * <p>Most calls will be handled by a single thread, but that is not true for all calls. For example + * {@link #dump(IndentingPrintWriter, String[])}) may be called on a different thread concurrently + * with other operations so implementations must still handle thread safety. * * @hide */ diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java index 9c36c3921e3e..d1369a289428 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java @@ -23,6 +23,7 @@ import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_S import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED; import static android.app.timezonedetector.TimeZoneConfiguration.PROPERTY_AUTO_DETECTION_ENABLED; +import static android.app.timezonedetector.TimeZoneConfiguration.PROPERTY_GEO_DETECTION_ENABLED; import android.annotation.NonNull; import android.annotation.Nullable; @@ -98,6 +99,12 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat boolean isAutoDetectionEnabled(); /** + * Returns whether geolocation can be used for time zone detection when {@link + * #isAutoDetectionEnabled()} returns {@code true}. + */ + boolean isGeoDetectionEnabled(); + + /** * Returns true if the device has had an explicit time zone set. */ boolean isDeviceTimeZoneInitialized(); @@ -200,7 +207,15 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat */ @GuardedBy("this") private ArrayMapWithHistory<Integer, QualifiedTelephonyTimeZoneSuggestion> - mSuggestionBySlotIndex = new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE); + mTelephonySuggestionsBySlotIndex = + new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE); + + /** + * The latest geolocation suggestion received. + */ + @GuardedBy("this") + private ReferenceWithHistory<GeolocationTimeZoneSuggestion> mLatestGeoLocationSuggestion = + new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE); @GuardedBy("this") private final List<Dumpable> mDumpables = new ArrayList<>(); @@ -284,17 +299,26 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat private static boolean containsAutoTimeDetectionProperties( @NonNull TimeZoneConfiguration configuration) { - return configuration.hasProperty(PROPERTY_AUTO_DETECTION_ENABLED); + return configuration.hasProperty(PROPERTY_AUTO_DETECTION_ENABLED) + || configuration.hasProperty(PROPERTY_GEO_DETECTION_ENABLED); } @Override public synchronized void suggestGeolocationTimeZone( @NonNull GeolocationTimeZoneSuggestion suggestion) { + if (DBG) { + Slog.d(LOG_TAG, "Geolocation suggestion received. newSuggestion=" + suggestion); + } + Objects.requireNonNull(suggestion); + mLatestGeoLocationSuggestion.set(suggestion); - // TODO Implement this. - throw new UnsupportedOperationException( - "Geo-location time zone detection is not currently implemented"); + // Now perform auto time zone detection. The new suggestion may be used to modify the time + // zone setting. + if (mCallback.isGeoDetectionEnabled()) { + String reason = "New geolocation time zone suggested. suggestion=" + suggestion; + doAutoTimeZoneDetection(reason); + } } @Override @@ -332,12 +356,14 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat new QualifiedTelephonyTimeZoneSuggestion(suggestion, score); // Store the suggestion against the correct slotIndex. - mSuggestionBySlotIndex.put(suggestion.getSlotIndex(), scoredSuggestion); + mTelephonySuggestionsBySlotIndex.put(suggestion.getSlotIndex(), scoredSuggestion); // Now perform auto time zone detection. The new suggestion may be used to modify the time // zone setting. - String reason = "New telephony time suggested. suggestion=" + suggestion; - doAutoTimeZoneDetection(reason); + if (!mCallback.isGeoDetectionEnabled()) { + String reason = "New telephony time zone suggested. suggestion=" + suggestion; + doAutoTimeZoneDetection(reason); + } } private static int scoreTelephonySuggestion(@NonNull TelephonyTimeZoneSuggestion suggestion) { @@ -363,9 +389,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat } /** - * Finds the best available time zone suggestion from all slotIndexes. If it is high-enough - * quality and automatic time zone detection is enabled then it will be set on the device. The - * outcome can be that this strategy becomes / remains un-opinionated and nothing is set. + * Performs automatic time zone detection. */ @GuardedBy("this") private void doAutoTimeZoneDetection(@NonNull String detectionReason) { @@ -374,6 +398,62 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat return; } + // Use the right suggestions based on the current configuration. This check is potentially + // race-prone until this value is set via a call to TimeZoneDetectorStrategy. + if (mCallback.isGeoDetectionEnabled()) { + doGeolocationTimeZoneDetection(detectionReason); + } else { + doTelephonyTimeZoneDetection(detectionReason); + } + } + + /** + * Detects the time zone using the latest available geolocation time zone suggestion, if one is + * available. The outcome can be that this strategy becomes / remains un-opinionated and nothing + * is set. + */ + @GuardedBy("this") + private void doGeolocationTimeZoneDetection(@NonNull String detectionReason) { + GeolocationTimeZoneSuggestion latestGeolocationSuggestion = + mLatestGeoLocationSuggestion.get(); + if (latestGeolocationSuggestion == null) { + return; + } + + List<String> zoneIds = latestGeolocationSuggestion.getZoneIds(); + if (zoneIds == null || zoneIds.isEmpty()) { + // This means the client has become uncertain about the time zone or it is certain there + // is no known zone. In either case we must leave the existing time zone setting as it + // is. + return; + } + + // GeolocationTimeZoneSuggestion has no measure of quality. We assume all suggestions are + // reliable. + String zoneId; + + // Introduce bias towards the device's current zone when there are multiple zone suggested. + String deviceTimeZone = mCallback.getDeviceTimeZone(); + if (zoneIds.contains(deviceTimeZone)) { + if (DBG) { + Slog.d(LOG_TAG, + "Geo tz suggestion contains current device time zone. Applying bias."); + } + zoneId = deviceTimeZone; + } else { + zoneId = zoneIds.get(0); + } + setDeviceTimeZoneIfRequired(zoneId, detectionReason); + } + + /** + * Detects the time zone using the latest available telephony time zone suggestions. + * Finds the best available time zone suggestion from all slotIndexes. If it is high-enough + * quality and automatic time zone detection is enabled then it will be set on the device. The + * outcome can be that this strategy becomes / remains un-opinionated and nothing is set. + */ + @GuardedBy("this") + private void doTelephonyTimeZoneDetection(@NonNull String detectionReason) { QualifiedTelephonyTimeZoneSuggestion bestTelephonySuggestion = findBestTelephonySuggestion(); @@ -468,9 +548,9 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat // slotIndex and find the best. Note that we deliberately do not look at age: the caller can // rate-limit so age is not a strong indicator of confidence. Instead, the callers are // expected to withdraw suggestions they no longer have confidence in. - for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) { + for (int i = 0; i < mTelephonySuggestionsBySlotIndex.size(); i++) { QualifiedTelephonyTimeZoneSuggestion candidateSuggestion = - mSuggestionBySlotIndex.valueAt(i); + mTelephonySuggestionsBySlotIndex.valueAt(i); if (candidateSuggestion == null) { // Unexpected continue; @@ -505,14 +585,10 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat @Override public synchronized void handleAutoTimeZoneConfigChanged() { if (DBG) { - Slog.d(LOG_TAG, "handleTimeZoneDetectionChange() called"); - } - if (mCallback.isAutoDetectionEnabled()) { - // When the user enabled time zone detection, run the time zone detection and change the - // device time zone if possible. - String reason = "Auto time zone detection setting enabled."; - doAutoTimeZoneDetection(reason); + Slog.d(LOG_TAG, "handleAutoTimeZoneConfigChanged()"); } + + doAutoTimeZoneDetection("handleAutoTimeZoneConfigChanged()"); } @Override @@ -532,15 +608,21 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat ipw.println("mCallback.isDeviceTimeZoneInitialized()=" + mCallback.isDeviceTimeZoneInitialized()); ipw.println("mCallback.getDeviceTimeZone()=" + mCallback.getDeviceTimeZone()); + ipw.println("mCallback.isGeoDetectionEnabled()=" + mCallback.isGeoDetectionEnabled()); ipw.println("Time zone change log:"); ipw.increaseIndent(); // level 2 mTimeZoneChangesLog.dump(ipw); ipw.decreaseIndent(); // level 2 + ipw.println("Geolocation suggestion history:"); + ipw.increaseIndent(); // level 2 + mLatestGeoLocationSuggestion.dump(ipw); + ipw.decreaseIndent(); // level 2 + ipw.println("Telephony suggestion history:"); ipw.increaseIndent(); // level 2 - mSuggestionBySlotIndex.dump(ipw); + mTelephonySuggestionsBySlotIndex.dump(ipw); ipw.decreaseIndent(); // level 2 ipw.decreaseIndent(); // level 1 @@ -555,7 +637,15 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat @VisibleForTesting public synchronized QualifiedTelephonyTimeZoneSuggestion getLatestTelephonySuggestion( int slotIndex) { - return mSuggestionBySlotIndex.get(slotIndex); + return mTelephonySuggestionsBySlotIndex.get(slotIndex); + } + + /** + * A method used to inspect strategy state during tests. Not intended for general use. + */ + @VisibleForTesting + public GeolocationTimeZoneSuggestion getLatestGeolocationSuggestion() { + return mLatestGeoLocationSuggestion.get(); } /** diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 6adff0d32d88..0c85387be695 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -1156,7 +1156,7 @@ public class TrustManagerService extends SystemService { } private void enforceListenerPermission() { - mContext.enforceCallingPermission(Manifest.permission.TRUST_LISTENER, + mContext.enforceCallingOrSelfPermission(Manifest.permission.TRUST_LISTENER, "register trust listener"); } diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index b3ec849e6c90..8ccbbdd2265c 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -99,6 +99,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -1177,7 +1178,8 @@ public final class TvInputManagerService extends SystemService { final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, "createSession"); final long identity = Binder.clearCallingIdentity(); - StringBuilder sessionId = new StringBuilder(); + // Generate a unique session id with a random UUID. + String uniqueSessionId = UUID.randomUUID().toString(); try { synchronized (mLock) { if (userId != mCurrentUserId && !isRecordingSession) { @@ -1206,20 +1208,17 @@ public final class TvInputManagerService extends SystemService { return; } - // Create a unique session id with pid, uid and resolved user id - sessionId.append(callingUid).append(callingPid).append(resolvedUserId); - // Create a new session token and a session state. IBinder sessionToken = new Binder(); SessionState sessionState = new SessionState(sessionToken, info.getId(), info.getComponent(), isRecordingSession, client, seq, callingUid, - callingPid, resolvedUserId, sessionId.toString()); + callingPid, resolvedUserId, uniqueSessionId); // Add them to the global session state map of the current user. userState.sessionStateMap.put(sessionToken, sessionState); // Map the session id to the sessionStateMap in the user state - mSessionIdToSessionStateMap.put(sessionId.toString(), sessionState); + mSessionIdToSessionStateMap.put(uniqueSessionId, sessionState); // Also, add them to the session state map of the current service. serviceState.sessionTokens.add(sessionToken); diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java index 2b0fe8a2602b..2fc17fe65775 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java @@ -68,6 +68,11 @@ public final class ClientProfile { private Set<Integer> mUsingFrontendIds = new HashSet<>(); /** + * List of the client ids that share frontend with the current client. + */ + private Set<Integer> mShareFeClientIds = new HashSet<>(); + + /** * List of the Lnb ids that are used by the current client. */ private Set<Integer> mUsingLnbIds = new HashSet<>(); @@ -113,11 +118,7 @@ public final class ClientProfile { } public int getPriority() { - return mPriority; - } - - public int getNiceValue() { - return mNiceValue; + return mPriority - mNiceValue; } public void setGroupId(int groupId) { @@ -141,17 +142,38 @@ public final class ClientProfile { mUsingFrontendIds.add(frontendId); } + /** + * Update the set of client that share frontend with the current client. + * + * @param clientId the client to share the fe with the current client. + */ + public void shareFrontend(int clientId) { + mShareFeClientIds.add(clientId); + } + + /** + * Remove the given client id from the share frontend client id set. + * + * @param clientId the client to stop sharing the fe with the current client. + */ + public void stopSharingFrontend(int clientId) { + mShareFeClientIds.remove(clientId); + } + public Set<Integer> getInUseFrontendIds() { return mUsingFrontendIds; } + public Set<Integer> getShareFeClientIds() { + return mShareFeClientIds; + } + /** * Called when the client released a frontend. - * - * @param frontendId being released. */ - public void releaseFrontend(int frontendId) { - mUsingFrontendIds.remove(frontendId); + public void releaseFrontend() { + mUsingFrontendIds.clear(); + mShareFeClientIds.clear(); } /** @@ -201,6 +223,7 @@ public final class ClientProfile { */ public void reclaimAllResources() { mUsingFrontendIds.clear(); + mShareFeClientIds.clear(); mUsingLnbIds.clear(); mUsingCasSystemId = INVALID_RESOURCE_ID; } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index 7cb59dcbfb9b..fb2347e8e133 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -210,19 +210,36 @@ public class TunerResourceManagerService extends SystemService implements IBinde } synchronized (mLock) { if (!checkClientExists(request.getClientId())) { - throw new RemoteException("Request frontend from unregistered client:" + throw new RemoteException("Request frontend from unregistered client: " + request.getClientId()); } + // If the request client is holding or sharing a frontend, throw an exception. + if (!getClientProfile(request.getClientId()).getInUseFrontendIds().isEmpty()) { + throw new RemoteException("Release frontend before requesting another one. " + + "Client id: " + request.getClientId()); + } return requestFrontendInternal(request, frontendHandle); } } @Override - public void shareFrontend(int selfClientId, int targetClientId) { + public void shareFrontend(int selfClientId, int targetClientId) throws RemoteException { enforceTunerAccessPermission("shareFrontend"); enforceTrmAccessPermission("shareFrontend"); - if (DEBUG) { - Slog.d(TAG, "shareFrontend from " + selfClientId + " with " + targetClientId); + synchronized (mLock) { + if (!checkClientExists(selfClientId)) { + throw new RemoteException("Share frontend request from an unregistered client:" + + selfClientId); + } + if (!checkClientExists(targetClientId)) { + throw new RemoteException("Request to share frontend with an unregistered " + + "client:" + targetClientId); + } + if (getClientProfile(targetClientId).getInUseFrontendIds().isEmpty()) { + throw new RemoteException("Request to share frontend with a client that has no " + + "frontend resources. Target client id:" + targetClientId); + } + shareFrontendInternal(selfClientId, targetClientId); } } @@ -315,7 +332,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde throw new RemoteException( "Client is not the current owner of the releasing fe."); } - releaseFrontendInternal(fe); + releaseFrontendInternal(fe, clientId); } } @@ -649,6 +666,17 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @VisibleForTesting + protected void shareFrontendInternal(int selfClientId, int targetClientId) { + if (DEBUG) { + Slog.d(TAG, "shareFrontend from " + selfClientId + " with " + targetClientId); + } + for (int feId : getClientProfile(targetClientId).getInUseFrontendIds()) { + getClientProfile(selfClientId).useFrontend(feId); + } + getClientProfile(targetClientId).shareFrontend(selfClientId); + } + + @VisibleForTesting protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) { if (DEBUG) { Slog.d(TAG, "requestLnb(request=" + request + ")"); @@ -777,11 +805,17 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @VisibleForTesting - protected void releaseFrontendInternal(FrontendResource fe) { + protected void releaseFrontendInternal(FrontendResource fe, int clientId) { if (DEBUG) { - Slog.d(TAG, "releaseFrontend(id=" + fe.getId() + ")"); + Slog.d(TAG, "releaseFrontend(id=" + fe.getId() + ", clientId=" + clientId + " )"); + } + if (clientId == fe.getOwnerClientId()) { + ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId()); + for (int shareOwnerId : ownerClient.getShareFeClientIds()) { + clearFrontendAndClientMapping(getClientProfile(shareOwnerId)); + } } - updateFrontendClientMappingOnRelease(fe); + clearFrontendAndClientMapping(getClientProfile(clientId)); } @VisibleForTesting @@ -882,8 +916,21 @@ public class TunerResourceManagerService extends SystemService implements IBinde Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingClientId, e); return false; } + + // Reclaim all the resources of the share owners of the frontend that is used by the current + // resource reclaimed client. ClientProfile profile = getClientProfile(reclaimingClientId); - reclaimingResourcesFromClient(profile); + Set<Integer> shareFeClientIds = profile.getShareFeClientIds(); + for (int clientId : shareFeClientIds) { + try { + mListeners.get(clientId).getListener().onReclaimResources(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to reclaim resources on client " + clientId, e); + return false; + } + clearAllResourcesAndClientMapping(getClientProfile(clientId)); + } + clearAllResourcesAndClientMapping(profile); return true; } @@ -929,16 +976,6 @@ public class TunerResourceManagerService extends SystemService implements IBinde } } - private void updateFrontendClientMappingOnRelease(@NonNull FrontendResource releasingFrontend) { - ClientProfile ownerProfile = getClientProfile(releasingFrontend.getOwnerClientId()); - releasingFrontend.removeOwner(); - ownerProfile.releaseFrontend(releasingFrontend.getId()); - for (int exclusiveGroupMember : releasingFrontend.getExclusiveGroupMemberFeIds()) { - getFrontendResource(exclusiveGroupMember).removeOwner(); - ownerProfile.releaseFrontend(exclusiveGroupMember); - } - } - private void updateLnbClientMappingOnNewGrant(int grantingId, int ownerClientId) { LnbResource grantingLnb = getLnbResource(grantingId); ClientProfile ownerProfile = getClientProfile(ownerClientId); @@ -967,10 +1004,10 @@ public class TunerResourceManagerService extends SystemService implements IBinde } /** - * Get the owner client's priority from the resource id. + * Get the owner client's priority. * * @param clientId the owner client id. - * @return the priority of the owner client of the resource. + * @return the priority of the owner client. */ private int getOwnerClientPriority(int clientId) { return getClientProfile(clientId).getPriority(); @@ -1011,7 +1048,11 @@ public class TunerResourceManagerService extends SystemService implements IBinde return; } if (fe.isInUse()) { - releaseFrontendInternal(fe); + ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId()); + for (int shareOwnerId : ownerClient.getShareFeClientIds()) { + clearFrontendAndClientMapping(getClientProfile(shareOwnerId)); + } + clearFrontendAndClientMapping(ownerClient); } for (int excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) { getFrontendResource(excGroupmemberFeId) @@ -1093,21 +1134,37 @@ public class TunerResourceManagerService extends SystemService implements IBinde } private void removeClientProfile(int clientId) { - reclaimingResourcesFromClient(getClientProfile(clientId)); + for (int shareOwnerId : getClientProfile(clientId).getShareFeClientIds()) { + clearFrontendAndClientMapping(getClientProfile(shareOwnerId)); + } + clearAllResourcesAndClientMapping(getClientProfile(clientId)); mClientProfiles.remove(clientId); mListeners.remove(clientId); } - private void reclaimingResourcesFromClient(ClientProfile profile) { + private void clearFrontendAndClientMapping(ClientProfile profile) { for (Integer feId : profile.getInUseFrontendIds()) { - getFrontendResource(feId).removeOwner(); + FrontendResource fe = getFrontendResource(feId); + if (fe.getOwnerClientId() == profile.getId()) { + fe.removeOwner(); + continue; + } + getClientProfile(fe.getOwnerClientId()).stopSharingFrontend(profile.getId()); } + profile.releaseFrontend(); + } + + private void clearAllResourcesAndClientMapping(ClientProfile profile) { + // Clear Lnb for (Integer lnbId : profile.getInUseLnbIds()) { getLnbResource(lnbId).removeOwner(); } + // Clear Cas if (profile.getInUseCasSystemId() != ClientProfile.INVALID_RESOURCE_ID) { getCasResource(profile.getInUseCasSystemId()).removeOwner(profile.getId()); } + // Clear Frontend + clearFrontendAndClientMapping(profile); profile.reclaimAllResources(); } diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java index cdb61995c336..5772dea287fc 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java @@ -75,10 +75,31 @@ public interface UriGrantsManagerInternal { void removeUriPermissionsForPackage( String packageName, int userHandle, boolean persistable, boolean targetOnly); /** - * @param uri This uri must NOT contain an embedded userId. + * Remove any {@link UriPermission} associated with the owner whose values match the given + * filtering parameters. + * + * @param token An opaque owner token as returned by {@link #newUriPermissionOwner(String)}. + * @param uri This uri must NOT contain an embedded userId. {@code null} to apply to all Uris. + * @param mode The modes (as a bitmask) to revoke. * @param userId The userId in which the uri is to be resolved. */ void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId); + + /** + * Remove any {@link UriPermission} associated with the owner whose values match the given + * filtering parameters. + * + * @param token An opaque owner token as returned by {@link #newUriPermissionOwner(String)}. + * @param uri This uri must NOT contain an embedded userId. {@code null} to apply to all Uris. + * @param mode The modes (as a bitmask) to revoke. + * @param userId The userId in which the uri is to be resolved. + * @param targetPkg Calling package name to match, or {@code null} to apply to all packages. + * @param targetUserId Calling user to match, or {@link UserHandle#USER_ALL} to apply to all + * users. + */ + void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId, + String targetPkg, int targetUserId); + boolean checkAuthorityGrants( int callingUid, ProviderInfo cpi, int userId, boolean checkUser); void dump(PrintWriter pw, boolean dumpAll, String dumpPackage); diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index f5e1602ee6be..a106dc682208 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -51,7 +51,6 @@ import android.app.AppGlobals; import android.app.GrantedUriPermission; import android.app.IUriGrantsManager; import android.content.ClipData; -import android.content.ComponentName; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; @@ -88,11 +87,11 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.SystemServiceManager; -import libcore.io.IoUtils; - import com.google.android.collect.Lists; import com.google.android.collect.Maps; +import libcore.io.IoUtils; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -1431,16 +1430,18 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { @Override public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId) { + revokeUriPermissionFromOwner(token, uri, mode, userId, null, UserHandle.USER_ALL); + } + + @Override + public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId, + String targetPkg, int targetUserId) { final UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token); if (owner == null) { throw new IllegalArgumentException("Unknown owner: " + token); } - - if (uri == null) { - owner.removeUriPermissions(mode); - } else { - owner.removeUriPermission(new GrantUri(userId, uri, mode), mode); - } + GrantUri grantUri = uri == null ? null : new GrantUri(userId, uri, mode); + owner.removeUriPermission(grantUri, mode, targetPkg, targetUserId); } @Override diff --git a/services/core/java/com/android/server/uri/UriPermissionOwner.java b/services/core/java/com/android/server/uri/UriPermissionOwner.java index 2b404a43a338..0c263997a8b5 100644 --- a/services/core/java/com/android/server/uri/UriPermissionOwner.java +++ b/services/core/java/com/android/server/uri/UriPermissionOwner.java @@ -21,6 +21,7 @@ import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION; import android.os.Binder; import android.os.IBinder; +import android.os.UserHandle; import android.util.ArraySet; import android.util.proto.ProtoOutputStream; @@ -74,30 +75,47 @@ public class UriPermissionOwner { } void removeUriPermission(GrantUri grantUri, int mode) { + removeUriPermission(grantUri, mode, null, UserHandle.USER_ALL); + } + + void removeUriPermission(GrantUri grantUri, int mode, String targetPgk, int targetUserId) { if ((mode & FLAG_GRANT_READ_URI_PERMISSION) != 0 && mReadPerms != null) { Iterator<UriPermission> it = mReadPerms.iterator(); while (it.hasNext()) { UriPermission perm = it.next(); - if (grantUri == null || grantUri.equals(perm.uri)) { - perm.removeReadOwner(this); - mService.removeUriPermissionIfNeeded(perm); - it.remove(); + if (grantUri != null && !grantUri.equals(perm.uri)) { + continue; + } + if (targetPgk != null && !targetPgk.equals(perm.targetPkg)) { + continue; } + if (targetUserId != UserHandle.USER_ALL && targetUserId != perm.targetUserId) { + continue; + } + perm.removeReadOwner(this); + mService.removeUriPermissionIfNeeded(perm); + it.remove(); } if (mReadPerms.isEmpty()) { mReadPerms = null; } } - if ((mode & FLAG_GRANT_WRITE_URI_PERMISSION) != 0 - && mWritePerms != null) { + if ((mode & FLAG_GRANT_WRITE_URI_PERMISSION) != 0 && mWritePerms != null) { Iterator<UriPermission> it = mWritePerms.iterator(); while (it.hasNext()) { UriPermission perm = it.next(); - if (grantUri == null || grantUri.equals(perm.uri)) { - perm.removeWriteOwner(this); - mService.removeUriPermissionIfNeeded(perm); - it.remove(); + if (grantUri != null && !grantUri.equals(perm.uri)) { + continue; + } + if (targetPgk != null && !targetPgk.equals(perm.targetPkg)) { + continue; + } + if (targetUserId != UserHandle.USER_ALL && targetUserId != perm.targetUserId) { + continue; } + perm.removeWriteOwner(this); + mService.removeUriPermissionIfNeeded(perm); + it.remove(); } if (mWritePerms.isEmpty()) { mWritePerms = null; diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 7565d8f9647c..6e9526afa962 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -542,7 +542,7 @@ class ActivityMetricsLogger { + " processSwitch=" + processSwitch + " info=" + info); } - if (launchedActivity.mDrawn) { + if (launchedActivity.isReportedDrawn()) { // Launched activity is already visible. We cannot measure windows drawn delay. abort(info, "launched activity already visible"); return; @@ -681,7 +681,7 @@ class ActivityMetricsLogger { /** @return {@code true} if the given task has an activity will be drawn. */ private static boolean hasActivityToBeDrawn(Task t) { - return t.forAllActivities((r) -> r.mVisibleRequested && !r.mDrawn && !r.finishing); + return t.forAllActivities(r -> r.mVisibleRequested && !r.isReportedDrawn() && !r.finishing); } private void checkVisibility(Task t, ActivityRecord r) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5b8078729eee..64fa6ca590d2 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -499,7 +499,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // and reporting to the client that it is hidden. private boolean mSetToSleep; // have we told the activity to sleep? boolean nowVisible; // is this activity's window visible? - boolean mDrawn; // is this activity's window drawn? boolean mClientVisibilityDeferred;// was the visibility change message to client deferred? boolean idle; // has the activity gone idle? boolean hasBeenLaunched;// has this activity ever been launched? @@ -564,8 +563,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private boolean mClientVisible; boolean firstWindowDrawn; - // Last drawn state we reported to the app token. - private boolean reportedDrawn; + /** Whether the visible window(s) of this activity is drawn. */ + private boolean mReportedDrawn; private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults = new WindowState.UpdateReportedVisibilityResults(); @@ -927,7 +926,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A pw.println(prefix + "mVisibleRequested=" + mVisibleRequested + " mVisible=" + mVisible + " mClientVisible=" + mClientVisible + ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "") - + " reportedDrawn=" + reportedDrawn + " reportedVisible=" + reportedVisible); + + " reportedDrawn=" + mReportedDrawn + " reportedVisible=" + reportedVisible); if (paused) { pw.print(prefix); pw.print("paused="); pw.println(paused); } @@ -1213,14 +1212,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return task; } - /** - * Sets the Task on this activity for the purposes of re-use during launch where we will - * re-use another activity instead of this one for the launch. - */ - void setTaskForReuse(Task task) { - this.task = task; - } - Task getStack() { return task != null ? task.getRootTask() : null; } @@ -1591,7 +1582,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A keysPaused = false; inHistory = false; nowVisible = false; - mDrawn = false; mClientVisible = true; idle = false; hasBeenLaunched = false; @@ -2549,7 +2539,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final Task stack = getRootTask(); final boolean mayAdjustTop = (isState(RESUMED) || stack.mResumedActivity == null) - && stack.isFocusedStackOnDisplay(); + && stack.isFocusedStackOnDisplay() + // Do not adjust focus task because the task will be reused to launch new activity. + && !task.isClearingToReuseTask(); final boolean shouldAdjustGlobalFocus = mayAdjustTop // It must be checked before {@link #makeFinishingLocked} is called, because a stack // is not visible if it only contains finishing activities. @@ -5402,11 +5394,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** Called when the windows associated app window container are drawn. */ - void onWindowsDrawn(boolean drawn, long timestampNs) { - mDrawn = drawn; - if (!drawn) { - return; - } + private void onWindowsDrawn(long timestampNs) { final TransitionInfoSnapshot info = mStackSupervisor .getActivityMetricsLogger().notifyWindowsDrawn(this, timestampNs); final boolean validInfo = info != null; @@ -5512,7 +5500,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (!nowGone) { // If the app is not yet gone, then it can only become visible/drawn. if (!nowDrawn) { - nowDrawn = reportedDrawn; + nowDrawn = mReportedDrawn; } if (!nowVisible) { nowVisible = reportedVisible; @@ -5520,9 +5508,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting=" + numInteresting + " visible=" + numVisible); - if (nowDrawn != reportedDrawn) { - onWindowsDrawn(nowDrawn, SystemClock.elapsedRealtimeNanos()); - reportedDrawn = nowDrawn; + if (nowDrawn != mReportedDrawn) { + if (nowDrawn) { + onWindowsDrawn(SystemClock.elapsedRealtimeNanos()); + } + mReportedDrawn = nowDrawn; } if (nowVisible != reportedVisible) { if (DEBUG_VISIBILITY) Slog.v(TAG, @@ -5536,6 +5526,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + boolean isReportedDrawn() { + return mReportedDrawn; + } + boolean isClientVisible() { return mClientVisible; } @@ -7675,7 +7669,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A proto.write(VISIBLE_REQUESTED, mVisibleRequested); proto.write(CLIENT_VISIBLE, mClientVisible); proto.write(DEFER_HIDING_CLIENT, mDeferHidingClient); - proto.write(REPORTED_DRAWN, reportedDrawn); + proto.write(REPORTED_DRAWN, mReportedDrawn); proto.write(REPORTED_VISIBLE, reportedVisible); proto.write(NUM_INTERESTING_WINDOWS, mNumInterestingWindows); proto.write(NUM_DRAWN_WINDOWS, mNumDrawnWindows); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index f7cb0146ea52..f6158383d28a 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1766,8 +1766,8 @@ class ActivityStarter { } else if (mInTask != null) { return mInTask; } else { - final Task stack = getLaunchStack(mStartActivity, mLaunchFlags, - null /* task */, mOptions); + final Task stack = getLaunchStack(mStartActivity, mLaunchFlags, null /* task */, + mOptions); final ActivityRecord top = stack.getTopNonFinishingActivity(); if (top != null) { return top.getTask(); @@ -1870,13 +1870,7 @@ class ActivityStarter { return START_SUCCESS; } - boolean clearTaskForReuse = false; if (reusedTask != null) { - if (mStartActivity.getTask() == null) { - mStartActivity.setTaskForReuse(reusedTask); - clearTaskForReuse = true; - } - if (targetTask.intent == null) { // This task was started because of movement of the activity based on // affinity... @@ -1923,13 +1917,6 @@ class ActivityStarter { complyActivityFlags(targetTask, reusedTask != null ? reusedTask.getTopNonFinishingActivity() : null, intentGrants); - if (clearTaskForReuse) { - // Clear task for re-use so later code to methods - // {@link #setTaskFromReuseOrCreateNewTask}, {@link #setTaskFromSourceRecord}, or - // {@link #setTaskFromInTask} can parent it to the task. - mStartActivity.setTaskForReuse(null); - } - if (mAddingToTask) { return START_SUCCESS; } @@ -2023,8 +2010,6 @@ class ActivityStarter { // of history or if it is finished immediately), thus disassociating the task. Also note // that mReuseTask is reset as a result of {@link Task#performClearTaskLocked} // launching another activity. - // TODO(b/36119896): We shouldn't trigger activity launches in this path since we are - // already launching one. targetTask.performClearTaskLocked(); targetTask.setIntent(mStartActivity); mAddingToTask = true; @@ -2517,8 +2502,8 @@ class ActivityStarter { intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask()); } - final Task launchStack = - getLaunchStack(mStartActivity, mLaunchFlags, intentTask, mOptions); + final Task launchStack = getLaunchStack(mStartActivity, mLaunchFlags, intentTask, + mOptions); if (launchStack == null || launchStack == mTargetStack) { // Do not set mMovedToFront to true below for split-screen-top stack, or // START_TASK_TO_FRONT will be returned and trigger unexpected animations when a diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 31a9c5d4242c..403f225032e9 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -5347,15 +5347,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return mAmInternal.isBackgroundActivityStartsEnabled(); } - void enableScreenAfterBoot(boolean booted) { - writeBootProgressEnableScreen(SystemClock.uptimeMillis()); - mWindowManager.enableScreenAfterBoot(); - - synchronized (mGlobalLock) { - updateEventDispatchingLocked(booted); - } - } - static long getInputDispatchingTimeoutMillisLocked(ActivityRecord r) { if (r == null || !r.hasProcess()) { return DEFAULT_DISPATCHING_TIMEOUT_MILLIS; @@ -6449,9 +6440,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void enableScreenAfterBoot(boolean booted) { + writeBootProgressEnableScreen(SystemClock.uptimeMillis()); + mWindowManager.enableScreenAfterBoot(); synchronized (mGlobalLock) { - writeBootProgressEnableScreen(SystemClock.uptimeMillis()); - mWindowManager.enableScreenAfterBoot(); updateEventDispatchingLocked(booted); } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ba5a38290349..0215ead7e5de 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -117,7 +117,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.H.REPORT_FOCUS_CHANGE; import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE; -import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS; import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS; import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT; import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD; @@ -469,12 +468,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp WindowState mLastFocus = null; /** - * Windows that have lost input focus and are waiting for the new focus window to be displayed - * before they are told about this. - */ - ArrayList<WindowState> mLosingFocus = new ArrayList<>(); - - /** * The foreground app of this display. Windows below this app cannot be the focused window. If * the user taps on the area outside of the task of the focused app, we will notify AM about the * new task the user wants to interact with. @@ -899,10 +892,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } - if (!mLosingFocus.isEmpty() && w.isFocused() && w.isDisplayedLw()) { - mWmService.mH.obtainMessage(REPORT_LOSING_FOCUS, this).sendToTarget(); - } - w.updateResizingWindowIfNeeded(); }; @@ -1557,7 +1546,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // the heavy operations. This also benefits that the states of multiple activities // are handled together. r.linkFixedRotationTransform(prevRotatedLaunchingApp); - setFixedRotationLaunchingAppUnchecked(r, rotation); + if (r != mFixedRotationTransitionListener.mAnimatingRecents) { + // Only update the record for normal activity so the display orientation can be + // updated when the transition is done if it becomes the top. And the case of + // recents can be handled when the recents animation is finished. + setFixedRotationLaunchingAppUnchecked(r, rotation); + } return; } @@ -2919,21 +2913,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (mLastFocus != mCurrentFocus) { pw.print(" mLastFocus="); pw.println(mLastFocus); } - if (mLosingFocus.size() > 0) { - pw.println(); - pw.println(" Windows losing focus:"); - for (int i = mLosingFocus.size() - 1; i >= 0; i--) { - final WindowState w = mLosingFocus.get(i); - pw.print(" Losing #"); pw.print(i); pw.print(' '); - pw.print(w); - if (dumpAll) { - pw.println(":"); - w.dump(pw, " ", true); - } else { - pw.println(); - } - } - } pw.print(" mFocusedApp="); pw.println(mFocusedApp); if (mLastStatusBarVisibility != 0) { pw.print(" mLastStatusBarVisibility=0x"); @@ -3152,7 +3131,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4)); final WindowState oldFocus = mCurrentFocus; mCurrentFocus = newFocus; - mLosingFocus.remove(newFocus); if (newFocus != null) { mWinAddedSinceNullFocus.clear(); @@ -4004,9 +3982,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation)); - // TODO: Not sure if we really need to set the rotation here since we are updating from - // the display info above... - mDisplayFrames.mRotation = getRotation(); mDisplayPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode); int seq = mLayoutSeq + 1; diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index df5356303f8b..3c64ffb237d6 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -1627,8 +1627,8 @@ class RecentTasks { } if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding affilliates starting at " + topIndex + " from intial " + taskIndex); - // Find the end of the chain, doing a sanity check along the way. - boolean sane = top.mAffiliatedTaskId == task.mAffiliatedTaskId; + // Find the end of the chain, doing a validity check along the way. + boolean isValid = top.mAffiliatedTaskId == task.mAffiliatedTaskId; int endIndex = topIndex; Task prev = top; while (endIndex < recentsCount) { @@ -1640,7 +1640,7 @@ class RecentTasks { if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != INVALID_TASK_ID) { Slog.wtf(TAG, "Bad chain @" + endIndex + ": first task has next affiliate: " + prev); - sane = false; + isValid = false; break; } } else { @@ -1652,7 +1652,7 @@ class RecentTasks { + " has bad next affiliate " + cur.mNextAffiliate + " id " + cur.mNextAffiliateTaskId + ", expected " + prev); - sane = false; + isValid = false; break; } } @@ -1662,7 +1662,7 @@ class RecentTasks { Slog.wtf(TAG, "Bad chain @" + endIndex + ": last task " + cur + " has previous affiliate " + cur.mPrevAffiliate); - sane = false; + isValid = false; } if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: end of chain @" + endIndex); break; @@ -1673,7 +1673,7 @@ class RecentTasks { + ": task " + cur + " has previous affiliate " + cur.mPrevAffiliate + " but should be id " + cur.mPrevAffiliate); - sane = false; + isValid = false; break; } } @@ -1682,7 +1682,7 @@ class RecentTasks { + ": task " + cur + " has affiliated id " + cur.mAffiliatedTaskId + " but should be " + task.mAffiliatedTaskId); - sane = false; + isValid = false; break; } prev = cur; @@ -1690,18 +1690,18 @@ class RecentTasks { if (endIndex >= recentsCount) { Slog.wtf(TAG, "Bad chain ran off index " + endIndex + ": last task " + prev); - sane = false; + isValid = false; break; } } - if (sane) { + if (isValid) { if (endIndex < taskIndex) { Slog.wtf(TAG, "Bad chain @" + endIndex + ": did not extend to task " + task + " @" + taskIndex); - sane = false; + isValid = false; } } - if (sane) { + if (isValid) { // All looks good, we can just move all of the affiliated tasks // to the top. for (int i=topIndex; i<=endIndex; i++) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index b3a3ed7eeba4..aeaffd98f820 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -737,7 +737,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> winAnimator.mSession.mPid, operation); final long callingIdentity = Binder.clearCallingIdentity(); try { - // There was some problem...first, do a sanity check of the window list to make sure + // There was some problem...first, do a validity check of the window list to make sure // we haven't left any dangling surfaces around. Slog.i(TAG_WM, "Out of memory for surface! Looking for leaks..."); @@ -811,7 +811,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } // "Something has changed! Let's make it correct now." - // TODO: Super crazy long method that should be broken down... + // TODO: Super long method that should be broken down... void performSurfacePlacementNoTrace() { if (DEBUG_WINDOW_TRACE) Slog.v(TAG, "performSurfacePlacementInner: entry. Called by " + Debug.getCallers(3)); @@ -2816,7 +2816,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * @param launchParams The resolved launch params to use. * @param realCallingPid The pid from {@link ActivityStarter#setRealCallingPid} * @param realCallingUid The uid from {@link ActivityStarter#setRealCallingUid} - * * @return The stack to use for the launch or INVALID_STACK_ID. */ Task getLaunchStack(@Nullable ActivityRecord r, @@ -2965,10 +2964,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // If {@code r} is already in target display area and its task is the same as the candidate // task, the intention should be getting a launch stack for the reusable activity, so we can // use the existing stack. - if (candidateTask != null && (r.getTask() == null || r.getTask() == candidateTask)) { - // TODO(b/153920825): Fix incorrect evaluation of attached state - final TaskDisplayArea attachedTaskDisplayArea = r.getTask() != null - ? r.getTask().getDisplayArea() : r.getDisplayArea(); + if (candidateTask != null) { + final TaskDisplayArea attachedTaskDisplayArea = candidateTask.getDisplayArea(); if (attachedTaskDisplayArea == null || attachedTaskDisplayArea == taskDisplayArea) { return candidateTask.getRootTask(); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index ec7b1eed7f91..0529abf89f6e 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1836,14 +1836,25 @@ class Task extends WindowContainer<WindowContainer> { */ void performClearTaskLocked() { mReuseTask = true; - performClearTask("clear-task-all"); - mReuseTask = false; + mStackSupervisor.beginDeferResume(); + try { + performClearTask("clear-task-all"); + } finally { + mStackSupervisor.endDeferResume(); + mReuseTask = false; + } } ActivityRecord performClearTaskForReuseLocked(ActivityRecord newR, int launchFlags) { mReuseTask = true; - final ActivityRecord result = performClearTaskLocked(newR, launchFlags); - mReuseTask = false; + mStackSupervisor.beginDeferResume(); + final ActivityRecord result; + try { + result = performClearTaskLocked(newR, launchFlags); + } finally { + mStackSupervisor.endDeferResume(); + mReuseTask = false; + } return result; } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 68445f6970fb..dbbb7ff69b3b 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -27,7 +27,6 @@ import android.annotation.Nullable; import android.app.ActivityManager.TaskSnapshot; import android.content.pm.PackageManager; import android.graphics.Bitmap; -import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.RecordingCanvas; @@ -82,7 +81,7 @@ class TaskSnapshotController { /** * Return value for {@link #getSnapshotMode}: We are not allowed to take a real screenshot but - * we should try to use the app theme to create a dummy representation of the app. + * we should try to use the app theme to create a fake representation of the app. */ @VisibleForTesting static final int SNAPSHOT_MODE_APP_THEME = 1; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b33a8e9ef5df..cd222a97f4d9 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3612,7 +3612,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG_WM, ">>> showStrictModeViolation"); - // TODO: Modify this to use the surface trace once it is not going crazy. + // TODO: Modify this to use the surface trace once it is not going baffling. // b/31532461 // TODO(multi-display): support multiple displays if (mStrictModeFlash == null) { @@ -4740,7 +4740,6 @@ public class WindowManagerService extends IWindowManager.Stub final class H extends android.os.Handler { public static final int REPORT_FOCUS_CHANGE = 2; - public static final int REPORT_LOSING_FOCUS = 3; public static final int WINDOW_FREEZE_TIMEOUT = 11; public static final int PERSIST_ANIMATION_SCALE = 14; @@ -4815,11 +4814,6 @@ public class WindowManagerService extends IWindowManager.Stub ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus moving from %s" + " to %s displayId=%d", lastFocus, newFocus, displayContent.getDisplayId()); - if (newFocus != null && lastFocus != null && !newFocus.isDisplayedLw()) { - ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Delaying loss of focus..."); - displayContent.mLosingFocus.add(lastFocus); - lastFocus = null; - } } // First notify the accessibility manager for the change so it has @@ -4842,24 +4836,6 @@ public class WindowManagerService extends IWindowManager.Stub break; } - case REPORT_LOSING_FOCUS: { - final DisplayContent displayContent = (DisplayContent) msg.obj; - ArrayList<WindowState> losers; - - synchronized (mGlobalLock) { - losers = displayContent.mLosingFocus; - displayContent.mLosingFocus = new ArrayList<>(); - } - - final int N = losers.size(); - for (int i = 0; i < N; i++) { - ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Losing delayed focus: %s", - losers.get(i)); - losers.get(i).reportFocusChangedSerialized(false); - } - break; - } - case WINDOW_FREEZE_TIMEOUT: { final DisplayContent displayContent = (DisplayContent) msg.obj; synchronized (mGlobalLock) { diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index ab6e35b452a0..c714eeb92e68 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -196,7 +196,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio private final Configuration mLastReportedConfiguration = new Configuration(); // Configuration that is waiting to be dispatched to the process. private Configuration mPendingConfiguration; - private final Configuration mNewOverrideConfig = new Configuration(); // Registered display id as a listener to override config change private int mDisplayId; private ActivityRecord mConfigActivityRecord; @@ -1292,11 +1291,26 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } @Override + public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { + super.onRequestedOverrideConfigurationChanged( + sanitizeProcessConfiguration(overrideConfiguration)); + } + + @Override public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) { + super.onRequestedOverrideConfigurationChanged( + sanitizeProcessConfiguration(mergedOverrideConfig)); + } + + private static Configuration sanitizeProcessConfiguration(Configuration config) { // Make sure that we don't accidentally override the activity type. - mNewOverrideConfig.setTo(mergedOverrideConfig); - mNewOverrideConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); - super.onRequestedOverrideConfigurationChanged(mNewOverrideConfig); + if (config.windowConfiguration.getActivityType() != ACTIVITY_TYPE_UNDEFINED) { + final Configuration sanitizedConfig = new Configuration(config); + sanitizedConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); + return sanitizedConfig; + } + + return config; } private void updateConfiguration() { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index ebbd74aa7d1e..49e623d8dd11 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2456,9 +2456,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP finishInputEvent(event, true); } } - /** - * Dummy event receiver for windows that died visible. - */ + /** Fake event receiver for windows that died visible. */ private DeadWindowEventReceiver mDeadWindowEventReceiver; void openInputChannel(InputChannel outInputChannel) { @@ -2476,9 +2474,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mClientChannel.dispose(); mClientChannel = null; } else { - // If the window died visible, we setup a dummy input channel, so that taps + // If the window died visible, we setup a fake input channel, so that taps // can still detected by input monitor channel, and we can relaunch the app. - // Create dummy event receiver that simply reports all events as handled. + // Create fake event receiver that simply reports all events as handled. mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel); } mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this); diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 16edb55939a3..92177abbbf85 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -560,9 +560,9 @@ class WindowStateAnimator { } } - // Something is wrong and SurfaceFlinger will not like this, try to revert to sane values. - // This doesn't necessarily mean that there is an error in the system. The sizes might be - // incorrect, because it is before the first layout or draw. + // Something is wrong and SurfaceFlinger will not like this, try to revert to reasonable + // values. This doesn't necessarily mean that there is an error in the system. The sizes + // might be incorrect, because it is before the first layout or draw. if (outSize.width() < 1) { outSize.right = 1; } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index f7cd37fb6748..bc4d9a973ecb 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -548,7 +548,7 @@ class WindowToken extends WindowContainer<WindowState> { void applyFixedRotationTransform(DisplayInfo info, DisplayFrames displayFrames, Configuration config) { if (mFixedRotationTransformState != null) { - return; + cleanUpFixedRotationTransformState(true /* replacing */); } mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames, new Configuration(config), mDisplayContent.getRotation()); @@ -565,13 +565,13 @@ class WindowToken extends WindowContainer<WindowState> { * one. This takes the same effect as {@link #applyFixedRotationTransform}. */ void linkFixedRotationTransform(WindowToken other) { - if (mFixedRotationTransformState != null) { - return; - } final FixedRotationTransformState fixedRotationState = other.mFixedRotationTransformState; - if (fixedRotationState == null) { + if (fixedRotationState == null || mFixedRotationTransformState == fixedRotationState) { return; } + if (mFixedRotationTransformState != null) { + cleanUpFixedRotationTransformState(true /* replacing */); + } mFixedRotationTransformState = fixedRotationState; fixedRotationState.mAssociatedTokens.add(this); onConfigurationChanged(getParent().getConfiguration()); @@ -626,11 +626,17 @@ class WindowToken extends WindowContainer<WindowState> { // The state is cleared at the end, because it is used to indicate that other windows can // use seamless rotation when applying rotation to display. for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) { - state.mAssociatedTokens.get(i).cleanUpFixedRotationTransformState(); + state.mAssociatedTokens.get(i).cleanUpFixedRotationTransformState( + false /* replacing */); } } - private void cleanUpFixedRotationTransformState() { + private void cleanUpFixedRotationTransformState(boolean replacing) { + if (replacing && mFixedRotationTransformState.mAssociatedTokens.size() > 1) { + // The state is not only used by self. Make sure to leave the influence by others. + mFixedRotationTransformState.mAssociatedTokens.remove(this); + mFixedRotationTransformState.mRotatedContainers.remove(this); + } mFixedRotationTransformState = null; notifyFixedRotationTransform(false /* enabled */); } diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp index b6633ced771b..b3f3a5e1ff72 100644 --- a/services/core/jni/com_android_server_VibratorService.cpp +++ b/services/core/jni/com_android_server_VibratorService.cpp @@ -17,9 +17,7 @@ #define LOG_TAG "VibratorService" #include <android/hardware/vibrator/1.3/IVibrator.h> -#include <android/hardware/vibrator/BnVibratorCallback.h> #include <android/hardware/vibrator/IVibrator.h> -#include <binder/IServiceManager.h> #include "jni.h" #include <nativehelper/JNIHelp.h> @@ -28,19 +26,11 @@ #include <utils/misc.h> #include <utils/Log.h> -#include <hardware/vibrator.h> #include <inttypes.h> -#include <stdio.h> #include <vibratorservice/VibratorHalController.h> -using android::hardware::Return; -using android::hardware::Void; -using android::hardware::vibrator::V1_0::EffectStrength; -using android::hardware::vibrator::V1_0::Status; -using android::hardware::vibrator::V1_1::Effect_1_1; - namespace V1_0 = android::hardware::vibrator::V1_0; namespace V1_1 = android::hardware::vibrator::V1_1; namespace V1_2 = android::hardware::vibrator::V1_2; @@ -87,150 +77,7 @@ static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) == static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) == static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK)); -class VibratorCallback { - public: - VibratorCallback(JNIEnv *env, jobject vibration) : - mVibration(MakeGlobalRefOrDie(env, vibration)) {} - - ~VibratorCallback() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->DeleteGlobalRef(mVibration); - } - - void onComplete() { - auto env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod(mVibration, sMethodIdOnComplete); - } - - private: - jobject mVibration; -}; - -class AidlVibratorCallback : public aidl::BnVibratorCallback { - public: - AidlVibratorCallback(JNIEnv *env, jobject vibration) : - mCb(env, vibration) {} - - binder::Status onComplete() override { - mCb.onComplete(); - return binder::Status::ok(); // oneway, local call - } - - private: - VibratorCallback mCb; -}; - -static constexpr int NUM_TRIES = 2; - -template<class R> -inline R NoneStatus() { - using ::android::hardware::Status; - return Status::fromExceptionCode(Status::EX_NONE); -} - -template<> -inline binder::Status NoneStatus() { - using binder::Status; - return Status::fromExceptionCode(Status::EX_NONE); -} - -// Creates a Return<R> with STATUS::EX_NULL_POINTER. -template<class R> -inline R NullptrStatus() { - using ::android::hardware::Status; - return Status::fromExceptionCode(Status::EX_NULL_POINTER); -} - -template<> -inline binder::Status NullptrStatus() { - using binder::Status; - return Status::fromExceptionCode(Status::EX_NULL_POINTER); -} - -template <typename I> -sp<I> getService() { - return I::getService(); -} - -template <> -sp<aidl::IVibrator> getService() { - return waitForVintfService<aidl::IVibrator>(); -} - -template <typename I> -sp<I> tryGetService() { - return I::tryGetService(); -} - -template <> -sp<aidl::IVibrator> tryGetService() { - return checkVintfService<aidl::IVibrator>(); -} - -template <typename I> -class HalWrapper { - public: - static std::unique_ptr<HalWrapper> Create() { - // Assume that if getService returns a nullptr, HAL is not available on the - // device. - auto hal = getService<I>(); - return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr; - } - - // Helper used to transparently deal with the vibrator HAL becoming unavailable. - template<class R, class... Args0, class... Args1> - R call(R (I::* fn)(Args0...), Args1&&... args1) { - // Return<R> doesn't have a default constructor, so make a Return<R> with - // STATUS::EX_NONE. - R ret{NoneStatus<R>()}; - - // Note that ret is guaranteed to be changed after this loop. - for (int i = 0; i < NUM_TRIES; ++i) { - ret = (mHal == nullptr) ? NullptrStatus<R>() - : (*mHal.*fn)(std::forward<Args1>(args1)...); - - if (ret.isOk()) { - break; - } - - ALOGE("Failed to issue command to vibrator HAL. Retrying."); - - // Restoring connection to the HAL. - mHal = tryGetService<I>(); - } - return ret; - } - - private: - HalWrapper(sp<I> &&hal) : mHal(std::move(hal)) {} - - private: - sp<I> mHal; -}; - -template <typename I> -static auto getHal() { - static auto sHalWrapper = HalWrapper<I>::Create(); - return sHalWrapper.get(); -} - -template<class R, class I, class... Args0, class... Args1> -R halCall(R (I::* fn)(Args0...), Args1&&... args1) { - auto hal = getHal<I>(); - return hal ? hal->call(fn, std::forward<Args1>(args1)...) : NullptrStatus<R>(); -} - -template<class R> -bool isValidEffect(jlong effect) { - if (effect < 0) { - return false; - } - R val = static_cast<R>(effect); - auto iter = hardware::hidl_enum_range<R>(); - return val >= *iter.begin() && val <= *std::prev(iter.end()); -} - -static void callVibrationOnComplete(jobject vibration) { +static inline void callVibrationOnComplete(jobject vibration) { if (vibration == nullptr) { return; } @@ -257,14 +104,6 @@ static void destroyVibratorController(void* rawVibratorController) { } static jlong vibratorInit(JNIEnv* /* env */, jclass /* clazz */) { - if (auto hal = getHal<aidl::IVibrator>()) { - // IBinder::pingBinder isn't accessible as a pointer function - // but getCapabilities can serve the same purpose - int32_t cap; - hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk(); - } else { - halCall(&V1_0::IVibrator::ping).isOk(); - } std::unique_ptr<vibrator::HalController> controller = std::make_unique<vibrator::HalController>(); controller->init(); @@ -342,70 +181,37 @@ static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jl return effects; } -static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong effect, jlong strength, - jobject vibration, jboolean withCallback) { - if (auto hal = getHal<aidl::IVibrator>()) { - int32_t lengthMs; - sp<AidlVibratorCallback> effectCallback = - (withCallback != JNI_FALSE ? new AidlVibratorCallback(env, vibration) : nullptr); - aidl::Effect effectType(static_cast<aidl::Effect>(effect)); - aidl::EffectStrength effectStrength(static_cast<aidl::EffectStrength>(strength)); - - auto status = hal->call(&aidl::IVibrator::perform, effectType, effectStrength, effectCallback, &lengthMs); - if (!status.isOk()) { - if (status.exceptionCode() != binder::Status::EX_UNSUPPORTED_OPERATION) { - ALOGE("Failed to perform haptic effect: effect=%" PRId64 ", strength=%" PRId32 - ": %s", static_cast<int64_t>(effect), static_cast<int32_t>(strength), status.toString8().string()); - } - return -1; - } - return lengthMs; - } else { - Status status; - uint32_t lengthMs; - auto callback = [&status, &lengthMs](Status retStatus, uint32_t retLengthMs) { - status = retStatus; - lengthMs = retLengthMs; - }; - EffectStrength effectStrength(static_cast<EffectStrength>(strength)); - - Return<void> ret; - if (isValidEffect<V1_0::Effect>(effect)) { - ret = halCall(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(effect), - effectStrength, callback); - } else if (isValidEffect<Effect_1_1>(effect)) { - ret = halCall(&V1_1::IVibrator::perform_1_1, static_cast<Effect_1_1>(effect), - effectStrength, callback); - } else if (isValidEffect<V1_2::Effect>(effect)) { - ret = halCall(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(effect), - effectStrength, callback); - } else if (isValidEffect<V1_3::Effect>(effect)) { - ret = halCall(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(effect), - effectStrength, callback); - } else { - ALOGW("Unable to perform haptic effect, invalid effect ID (%" PRId32 ")", - static_cast<int32_t>(effect)); - return -1; - } - - if (!ret.isOk()) { - ALOGW("Failed to perform effect (%" PRId32 ")", static_cast<int32_t>(effect)); - return -1; - } - - if (status == Status::OK) { - return lengthMs; - } else if (status != Status::UNSUPPORTED_OPERATION) { - // Don't warn on UNSUPPORTED_OPERATION, that's a normal event and just means the motor - // doesn't have a pre-defined waveform to perform for it, so we should just give the - // opportunity to fall back to the framework waveforms. - ALOGE("Failed to perform haptic effect: effect=%" PRId64 ", strength=%" PRId32 - ", error=%" PRIu32 ").", static_cast<int64_t>(effect), - static_cast<int32_t>(strength), static_cast<uint32_t>(status)); - } +static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */, + jlong controllerPtr) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorGetSupportedPrimitives failed because controller was not initialized"); + return nullptr; + } + auto result = controller->getSupportedPrimitives(); + if (!result.isOk()) { + return nullptr; } + std::vector<aidl::CompositePrimitive> supportedPrimitives = result.value(); + jintArray primitives = env->NewIntArray(supportedPrimitives.size()); + env->SetIntArrayRegion(primitives, 0, supportedPrimitives.size(), + reinterpret_cast<jint*>(supportedPrimitives.data())); + return primitives; +} - return -1; +static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, + jlong effect, jlong strength, jobject vibration) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorPerformEffect failed because controller was not initialized"); + return -1; + } + aidl::Effect effectType = static_cast<aidl::Effect>(effect); + aidl::EffectStrength effectStrength = static_cast<aidl::EffectStrength>(strength); + jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration); + auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); }; + auto result = controller->performEffect(effectType, effectStrength, callback); + return result.isOk() ? result.value().count() : -1; } static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, @@ -464,13 +270,14 @@ static const JNINativeMethod method_table[] = { {"vibratorOn", "(JJLcom/android/server/VibratorService$Vibration;)V", (void*)vibratorOn}, {"vibratorOff", "(J)V", (void*)vibratorOff}, {"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude}, - {"vibratorPerformEffect", "(JJLcom/android/server/VibratorService$Vibration;Z)J", + {"vibratorPerformEffect", "(JJJLcom/android/server/VibratorService$Vibration;)J", (void*)vibratorPerformEffect}, {"vibratorPerformComposedEffect", "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/" "VibratorService$Vibration;)V", (void*)vibratorPerformComposedEffect}, {"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects}, + {"vibratorGetSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives}, {"vibratorSetExternalControl", "(JZ)V", (void*)vibratorSetExternalControl}, {"vibratorGetCapabilities", "(J)J", (void*)vibratorGetCapabilities}, {"vibratorAlwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable}, diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java b/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java new file mode 100644 index 000000000000..5193fa85d238 --- /dev/null +++ b/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2020 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 com.android.server.devicepolicy; + +import android.annotation.Nullable; +import android.content.ComponentName; +import android.os.UserHandle; + +/** + * Caller identity containing the caller's UID, package name and component name. + * All parameters are verified on object creation unless the component name is null and the + * caller is a delegate. + */ +class CallerIdentity { + + private final int mUid; + @Nullable + private final String mPackageName; + @Nullable + private final ComponentName mComponentName; + + CallerIdentity(int uid, @Nullable String packageName, @Nullable ComponentName componentName) { + mUid = uid; + mPackageName = packageName; + mComponentName = componentName; + } + + public int getUid() { + return mUid; + } + + public int getUserId() { + return UserHandle.getUserId(mUid); + } + + public UserHandle getUserHandle() { + return UserHandle.getUserHandleForUid(mUid); + } + + @Nullable public String getPackageName() { + return mPackageName; + } + + @Nullable public ComponentName getComponentName() { + return mComponentName; + } +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index cafd56e5198b..6154bef2bda3 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1564,6 +1564,40 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } /** + * Creates a new {@link CallerIdentity} object to represent the caller's identity. + */ + private CallerIdentity getCallerIdentity(String callerPackage) { + final int callerUid = mInjector.binderGetCallingUid(); + + if (!isCallingFromPackage(callerPackage, callerUid)) { + throw new SecurityException( + String.format("Caller with uid %d is not %s", callerUid, callerPackage)); + } + + return new CallerIdentity(callerUid, callerPackage, null); + } + + /** + * Creates a new {@link CallerIdentity} object to represent the caller's identity. + * The component name should be an active admin for the calling user. + */ + private CallerIdentity getCallerIdentity(@NonNull ComponentName adminComponent) { + final int callerUid = mInjector.binderGetCallingUid(); + final DevicePolicyData policy = getUserData(UserHandle.getUserId(callerUid)); + ActiveAdmin admin = policy.mAdminMap.get(adminComponent); + + if (admin == null) { + throw new SecurityException(String.format("No active admin for %s", adminComponent)); + } + if (admin.getUid() != callerUid) { + throw new SecurityException( + String.format("Admin %s is not owned by uid %d", adminComponent, callerUid)); + } + + return new CallerIdentity(callerUid, adminComponent.getPackageName(), adminComponent); + } + + /** * Checks if the device is in COMP mode, and if so migrates it to managed profile on a * corporate owned device. */ @@ -4556,12 +4590,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private void enforceDeviceOwner(ComponentName who) { - synchronized (getLockObject()) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); - } - } - private void enforceProfileOrDeviceOwner(ComponentName who) { synchronized (getLockObject()) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); @@ -5161,20 +5189,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); Preconditions.checkStringNotEmpty(delegatePackage, "Delegate package is null or empty"); Preconditions.checkCollectionElementsNotNull(scopeList, "Scopes"); + final CallerIdentity identity = getCallerIdentity(who); + // Remove possible duplicates. final ArrayList<String> scopes = new ArrayList(new ArraySet(scopeList)); // Ensure given scopes are valid. if (scopes.retainAll(Arrays.asList(DELEGATIONS))) { throw new IllegalArgumentException("Unexpected delegation scopes"); } - final boolean hasDoDelegation = !Collections.disjoint(scopes, DEVICE_OWNER_DELEGATIONS); // Retrieve the user ID of the calling process. - final int userId = mInjector.userHandleGetCallingUserId(); + final int userId = identity.getUserId(); + final boolean hasDoDelegation = !Collections.disjoint(scopes, DEVICE_OWNER_DELEGATIONS); synchronized (getLockObject()) { // Ensure calling process is device/profile owner. if (hasDoDelegation) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); } else { + // TODO move whole condition out of synchronized block getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); } // Ensure the delegate is installed (skip this for DELEGATION_CERT_INSTALL in pre-N). @@ -6166,7 +6197,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void setRecommendedGlobalProxy(ComponentName who, ProxyInfo proxyInfo) { - enforceDeviceOwner(who); + Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); mInjector.binderWithCleanCallingIdentity( () -> mInjector.getConnectivityManager().setGlobalProxy(proxyInfo)); } @@ -6587,6 +6620,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); + // Allow setting this policy to true only if there is a split system user. if (forceEphemeralUsers && !mInjector.userManagerIsSplitSystemUser()) { throw new UnsupportedOperationException( @@ -6594,11 +6630,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } boolean removeAllUsers = false; synchronized (getLockObject()) { - final ActiveAdmin deviceOwner = - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); if (deviceOwner.forceEphemeralUsers != forceEphemeralUsers) { deviceOwner.forceEphemeralUsers = forceEphemeralUsers; - saveSettingsLocked(mInjector.userHandleGetCallingUserId()); + saveSettingsLocked(identity.getUserId()); mUserManagerInternal.setForceEphemeralUsers(forceEphemeralUsers); removeAllUsers = forceEphemeralUsers; } @@ -6614,21 +6649,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); + synchronized (getLockObject()) { - final ActiveAdmin deviceOwner = - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); return deviceOwner.forceEphemeralUsers; } } - private void ensureDeviceOwnerAndAllUsersAffiliated(ComponentName who) - throws SecurityException { - synchronized (getLockObject()) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); - } - ensureAllUsersAffiliated(); - } - private void ensureAllUsersAffiliated() throws SecurityException { synchronized (getLockObject()) { if (!areAllUsersAffiliatedWithDeviceLocked()) { @@ -6643,11 +6672,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } Objects.requireNonNull(who, "ComponentName is null"); - // TODO: If an unaffiliated user is removed, the admin will be able to request a bugreport // which could still contain data related to that user. Should we disallow that, e.g. until // next boot? Might not be needed given that this still requires user consent. - ensureDeviceOwnerAndAllUsersAffiliated(who); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); + ensureAllUsersAffiliated(); if (mRemoteBugreportServiceIsActive.get() || (getDeviceOwnerRemoteBugreportUri() != null)) { @@ -7171,6 +7201,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + private boolean isDeviceOwner(CallerIdentity identity) { + synchronized (getLockObject()) { + return mOwners.hasDeviceOwner() + && mOwners.getDeviceOwnerUserId() == identity.getUserId() + && mOwners.getDeviceOwnerComponent().equals(identity.getComponentName()); + } + } + private boolean isDeviceOwnerPackage(String packageName, int userId) { synchronized (getLockObject()) { return mOwners.hasDeviceOwner() @@ -8448,6 +8486,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void setDefaultSmsApplication(ComponentName admin, String packageName, boolean parent) { Objects.requireNonNull(admin, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(admin); if (parent) { ActiveAdmin ap = getActiveAdminForCallerLocked(admin, @@ -8456,7 +8495,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mInjector.binderWithCleanCallingIdentity(() -> enforcePackageIsSystemPackage( packageName, getProfileParentId(mInjector.userHandleGetCallingUserId()))); } else { - enforceDeviceOwner(admin); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); } mInjector.binderWithCleanCallingIdentity(() -> @@ -9218,14 +9257,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public boolean removeUser(ComponentName who, UserHandle userHandle) { Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(userHandle, "UserHandle is null"); - enforceDeviceOwner(who); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); - final int callingUserId = mInjector.userHandleGetCallingUserId(); return mInjector.binderWithCleanCallingIdentity(() -> { String restriction = isManagedProfile(userHandle.getIdentifier()) ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE : UserManager.DISALLOW_REMOVE_USER; - if (isAdminAffectedByRestriction(who, restriction, callingUserId)) { + if (isAdminAffectedByRestriction(who, restriction, identity.getUserId())) { Log.w(LOG_TAG, "The device owner cannot remove a user because " + restriction + " is enabled, and was not set by the device owner"); return false; @@ -9251,10 +9290,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean switchUser(ComponentName who, UserHandle userHandle) { Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); synchronized (getLockObject()) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); - long id = mInjector.binderClearCallingIdentity(); try { int userId = UserHandle.USER_SYSTEM; @@ -9275,7 +9314,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public int startUserInBackground(ComponentName who, UserHandle userHandle) { Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(userHandle, "UserHandle is null"); - enforceDeviceOwner(who); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); final int userId = userHandle.getIdentifier(); if (isManagedProfile(userId)) { @@ -9307,7 +9347,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public int stopUser(ComponentName who, UserHandle userHandle) { Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(userHandle, "UserHandle is null"); - enforceDeviceOwner(who); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); final int userId = userHandle.getIdentifier(); if (isManagedProfile(userId)) { @@ -9375,7 +9416,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public List<UserHandle> getSecondaryUsers(ComponentName who) { Objects.requireNonNull(who, "ComponentName is null"); - enforceDeviceOwner(who); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); return mInjector.binderWithCleanCallingIdentity(() -> { final List<UserInfo> userInfos = mInjector.getUserManager().getUsers(true @@ -10337,6 +10379,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void setGlobalSetting(ComponentName who, String setting, String value) { Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_GLOBAL_SETTING) @@ -10345,8 +10389,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .write(); synchronized (getLockObject()) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); - // Some settings are no supported any more. However we do not want to throw a // SecurityException to avoid breaking apps. if (GLOBAL_SETTINGS_DEPRECATED.contains(setting)) { @@ -10427,20 +10469,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void setLocationEnabled(ComponentName who, boolean locationEnabled) { - enforceDeviceOwner(Objects.requireNonNull(who)); - - UserHandle user = mInjector.binderGetCallingUserHandle(); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); mInjector.binderWithCleanCallingIdentity(() -> { boolean wasLocationEnabled = mInjector.getLocationManager().isLocationEnabledForUser( - user); - mInjector.getLocationManager().setLocationEnabledForUser(locationEnabled, user); + identity.getUserHandle()); + mInjector.getLocationManager().setLocationEnabledForUser(locationEnabled, + identity.getUserHandle()); // make a best effort to only show the notification if the admin is actually enabling // location. this is subject to race conditions with settings changes, but those are // unlikely to realistically interfere - if (locationEnabled && (wasLocationEnabled != locationEnabled)) { - showLocationSettingsEnabledNotification(user); + if (locationEnabled && !wasLocationEnabled) { + showLocationSettingsEnabledNotification(identity.getUserHandle()); } }); @@ -11967,16 +12009,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isSystemOnlyUser(ComponentName admin) { - enforceDeviceOwner(admin); - final int callingUserId = mInjector.userHandleGetCallingUserId(); - return UserManager.isSplitSystemUser() && callingUserId == UserHandle.USER_SYSTEM; + Objects.requireNonNull(admin, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(admin); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); + return UserManager.isSplitSystemUser() && identity.getUserId() == UserHandle.USER_SYSTEM; } @Override public void reboot(ComponentName admin) { - Objects.requireNonNull(admin); - // Make sure caller has DO. - enforceDeviceOwner(admin); + Objects.requireNonNull(admin, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(admin); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); + mInjector.binderWithCleanCallingIdentity(() -> { // Make sure there are no ongoing calls on the device. if (mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) { @@ -13482,18 +13526,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return; } - Objects.requireNonNull(admin); + Objects.requireNonNull(admin, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(admin); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); synchronized (getLockObject()) { - ActiveAdmin deviceOwner = - getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); - + ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); if (deviceOwner.isLogoutEnabled == enabled) { // already in the requested state return; } deviceOwner.isLogoutEnabled = enabled; - saveSettingsLocked(mInjector.userHandleGetCallingUserId()); + saveSettingsLocked(identity.getUserId()); } } @@ -13659,20 +13703,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return; } - Objects.requireNonNull(admin); + Objects.requireNonNull(admin, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(admin); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); final String startUserSessionMessageString = startUserSessionMessage != null ? startUserSessionMessage.toString() : null; synchronized (getLockObject()) { - final ActiveAdmin deviceOwner = - getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); - + final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); if (TextUtils.equals(deviceOwner.startUserSessionMessage, startUserSessionMessage)) { return; } deviceOwner.startUserSessionMessage = startUserSessionMessageString; - saveSettingsLocked(mInjector.userHandleGetCallingUserId()); + saveSettingsLocked(identity.getUserId()); } mInjector.getActivityManagerInternal() @@ -13684,20 +13728,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return; } - Objects.requireNonNull(admin); + Objects.requireNonNull(admin, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(admin); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); final String endUserSessionMessageString = endUserSessionMessage != null ? endUserSessionMessage.toString() : null; synchronized (getLockObject()) { - final ActiveAdmin deviceOwner = - getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); - + final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); if (TextUtils.equals(deviceOwner.endUserSessionMessage, endUserSessionMessage)) { return; } deviceOwner.endUserSessionMessage = endUserSessionMessageString; - saveSettingsLocked(mInjector.userHandleGetCallingUserId()); + saveSettingsLocked(identity.getUserId()); } mInjector.getActivityManagerInternal() @@ -13709,11 +13753,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return null; } - Objects.requireNonNull(admin); + Objects.requireNonNull(admin, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(admin); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); synchronized (getLockObject()) { - final ActiveAdmin deviceOwner = - getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); return deviceOwner.startUserSessionMessage; } } @@ -13723,11 +13768,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return null; } - Objects.requireNonNull(admin); + Objects.requireNonNull(admin, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(admin); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); synchronized (getLockObject()) { - final ActiveAdmin deviceOwner = - getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); return deviceOwner.endUserSessionMessage; } } @@ -13766,9 +13812,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature || !mHasTelephonyFeature) { return -1; } - Objects.requireNonNull(who, "ComponentName is null in addOverrideApn"); + Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(apnSetting, "ApnSetting is null in addOverrideApn"); - enforceDeviceOwner(who); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); if (tm != null) { @@ -13786,9 +13833,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature || !mHasTelephonyFeature) { return false; } - Objects.requireNonNull(who, "ComponentName is null in updateOverrideApn"); + Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(apnSetting, "ApnSetting is null in updateOverrideApn"); - enforceDeviceOwner(who); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); if (apnId < 0) { return false; @@ -13808,9 +13856,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature || !mHasTelephonyFeature) { return false; } - Objects.requireNonNull(who, "ComponentName is null in removeOverrideApn"); - enforceDeviceOwner(who); - + Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); return removeOverrideApnUnchecked(apnId); } @@ -13829,9 +13877,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature || !mHasTelephonyFeature) { return Collections.emptyList(); } - Objects.requireNonNull(who, "ComponentName is null in getOverrideApns"); - enforceDeviceOwner(who); - + Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); return getOverrideApnsUnchecked(); } @@ -13850,9 +13898,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature || !mHasTelephonyFeature) { return; } - Objects.requireNonNull(who, "ComponentName is null in setOverrideApnEnabled"); - enforceDeviceOwner(who); - + Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); setOverrideApnsEnabledUnchecked(enabled); } @@ -13868,8 +13916,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature || !mHasTelephonyFeature) { return false; } - Objects.requireNonNull(who, "ComponentName is null in isOverrideApnEnabled"); - enforceDeviceOwner(who); + Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); Cursor enforceCursor = mInjector.binderWithCleanCallingIdentity( () -> mContext.getContentResolver().query( @@ -13951,11 +14000,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return PRIVATE_DNS_SET_ERROR_FAILURE_SETTING; } - Objects.requireNonNull(who, "ComponentName is null"); - enforceDeviceOwner(who); - - final int returnCode; + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); switch (mode) { case PRIVATE_DNS_MODE_OPPORTUNISTIC: @@ -13989,9 +14036,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return PRIVATE_DNS_MODE_UNKNOWN; } - Objects.requireNonNull(who, "ComponentName is null"); - enforceDeviceOwner(who); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); + String currentMode = mInjector.settingsGlobalGetString(PRIVATE_DNS_MODE); if (currentMode == null) { currentMode = ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK; @@ -14013,10 +14061,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return null; } - Objects.requireNonNull(who, "ComponentName is null"); - enforceDeviceOwner(who); - + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); return mInjector.settingsGlobalGetString(PRIVATE_DNS_SPECIFIER); } @@ -14361,13 +14408,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void setUserControlDisabledPackages(ComponentName who, List<String> packages) { - Preconditions.checkNotNull(who, "ComponentName is null"); + Objects.requireNonNull(who, "ComponentName is null"); Preconditions.checkNotNull(packages, "packages is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); - enforceDeviceOwner(who); synchronized (getLockObject()) { - final int userHandle = mInjector.userHandleGetCallingUserId(); - setUserControlDisabledPackagesLocked(userHandle, packages); + setUserControlDisabledPackagesLocked(identity.getUserId(), packages); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_USER_CONTROL_DISABLED_PACKAGES) .setAdmin(who) @@ -14387,12 +14434,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public List<String> getUserControlDisabledPackages(ComponentName who) { - Preconditions.checkNotNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity)); - enforceDeviceOwner(who); - final int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier(); synchronized (getLockObject()) { - final List<String> packages = getUserData(userHandle).mUserControlDisabledPackages; + final List<String> packages = + getUserData(identity.getUserId()).mUserControlDisabledPackages; return packages == null ? Collections.EMPTY_LIST : packages; } } diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java index e8266a574bf5..b93c519ec8e4 100644 --- a/services/print/java/com/android/server/print/UserState.java +++ b/services/print/java/com/android/server/print/UserState.java @@ -30,6 +30,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -132,7 +133,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, private final Context mContext; - private final int mUserId; + private final @UserIdInt int mUserId; private final RemotePrintSpooler mSpooler; @@ -650,7 +651,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, mPrintServiceRecommendationsService = new RemotePrintServiceRecommendationService(mContext, - UserHandle.getUserHandleForUid(mUserId), this); + UserHandle.of(mUserId), this); } mPrintServiceRecommendationsChangeListenerRecords.add( new ListenerRecord<IRecommendationsChangeListener>(listener) { diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index bc75dcd91813..12c69eaa0f8d 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -150,7 +150,8 @@ public final class ProfcollectForwardingService extends SystemService { } // Sample for a fraction of app launches. - int traceFrequency = SystemProperties.getInt("profcollectd.applaunch_trace_freq", 2); + int traceFrequency = + SystemProperties.getInt("persist.profcollectd.applaunch_trace_freq", 2); int randomNum = ThreadLocalRandom.current().nextInt(100); if (randomNum < traceFrequency) { try { diff --git a/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java b/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java index 27a116c8043e..1586a33ba0e9 100644 --- a/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java +++ b/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java @@ -34,7 +34,8 @@ public final class SystemCaptionsManagerService extends context, com.android.internal.R.string.config_defaultSystemCaptionsManagerService), /*disallowProperty=*/ null, - /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_REFRESH_EAGER); + /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_REFRESH_EAGER + | PACKAGE_RESTART_POLICY_REFRESH_EAGER); } @Override diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp index e4e7e2288590..4f636efc7c06 100644 --- a/services/tests/PackageManagerServiceTests/host/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/Android.bp @@ -22,6 +22,7 @@ java_test_host { ], static_libs: [ "frameworks-base-hostutils", + "PackageManagerServiceHostTestsIntentVerifyUtils", ], test_suites: ["general-tests"], java_resources: [ @@ -33,7 +34,15 @@ java_test_host { ":PackageManagerTestAppVersion4", ":PackageManagerTestAppOriginalOverride", ":PackageManagerServiceDeviceSideTests", - ], + ":PackageManagerTestIntentVerifier", + ":PackageManagerTestIntentVerifierTarget1", + ":PackageManagerTestIntentVerifierTarget2", + ":PackageManagerTestIntentVerifierTarget3", + ":PackageManagerTestIntentVerifierTarget4Base", + ":PackageManagerTestIntentVerifierTarget4NoAutoVerify", + ":PackageManagerTestIntentVerifierTarget4Wildcard", + ":PackageManagerTestIntentVerifierTarget4WildcardNoAutoVerify", + ] } genrule { diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp new file mode 100644 index 000000000000..b7a0624e02b8 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp @@ -0,0 +1,19 @@ +// Copyright (C) 2020 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. + +java_library { + name: "PackageManagerServiceHostTestsIntentVerifyUtils", + srcs: ["src/**/*.kt"], + host_supported: true, +} diff --git a/tests/AutoVerify/app4/src/com/android/test/autoverify/MainActivity.java b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/IntentVerifyTestParams.kt index 09ef47212622..48119e0088c4 100644 --- a/tests/AutoVerify/app4/src/com/android/test/autoverify/MainActivity.java +++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/IntentVerifyTestParams.kt @@ -13,3 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +package com.android.server.pm.test.intent.verify + +interface IntentVerifyTestParams { + + val methodName: String + + fun toArgsMap(): Map<String, String> +} diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/SetActivityAsAlwaysParams.kt b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/SetActivityAsAlwaysParams.kt new file mode 100644 index 000000000000..26c3903b20d6 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/SetActivityAsAlwaysParams.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 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 com.android.server.pm.test.intent.verify + +data class SetActivityAsAlwaysParams( + val uri: String, + val packageName: String, + val activityName: String, + override val methodName: String = "setActivityAsAlways" +) : IntentVerifyTestParams { + + companion object { + private const val KEY_URI = "uri" + private const val KEY_PACKAGE_NAME = "packageName" + private const val KEY_ACTIVITY_NAME = "activityName" + + fun fromArgs(args: Map<String, String>) = SetActivityAsAlwaysParams( + args.getValue(KEY_URI), + args.getValue(KEY_PACKAGE_NAME), + args.getValue(KEY_ACTIVITY_NAME) + ) + } + + override fun toArgsMap() = mapOf( + KEY_URI to uri, + KEY_PACKAGE_NAME to packageName, + KEY_ACTIVITY_NAME to activityName + ) +} diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/StartActivityParams.kt b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/StartActivityParams.kt new file mode 100644 index 000000000000..7eddcfb621c4 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/StartActivityParams.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 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 com.android.server.pm.test.intent.verify + +data class StartActivityParams( + val uri: String, + val expected: List<String>, + val withBrowsers: Boolean = false, + override val methodName: String = "verifyActivityStart" +) : IntentVerifyTestParams { + companion object { + private const val KEY_URI = "uri" + private const val KEY_EXPECTED = "expected" + private const val KEY_BROWSER = "browser" + + fun fromArgs(args: Map<String, String>) = StartActivityParams( + args.getValue(KEY_URI), + args.getValue(KEY_EXPECTED).split(","), + args.getValue(KEY_BROWSER).toBoolean() + ) + } + + constructor( + uri: String, + expected: String, + withBrowsers: Boolean = false + ) : this(uri, listOf(expected), withBrowsers) + + override fun toArgsMap() = mapOf( + KEY_URI to uri, + KEY_EXPECTED to expected.joinToString(separator = ","), + KEY_BROWSER to withBrowsers.toString() + ) +} diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/VerifyRequest.kt b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/VerifyRequest.kt new file mode 100644 index 000000000000..f93b1e0e7e37 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/VerifyRequest.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 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 com.android.server.pm.test.intent.verify + +data class VerifyRequest( + val id: Int = -1, + val scheme: String, + val hosts: List<String>, + val packageName: String +) { + + companion object { + fun deserialize(value: String?): VerifyRequest { + val lines = value?.trim()?.lines() + ?: return VerifyRequest(scheme = "", hosts = emptyList(), packageName = "") + return VerifyRequest( + lines[0].removePrefix("id=").toInt(), + lines[1].removePrefix("scheme="), + lines[2].removePrefix("hosts=").split(","), + lines[3].removePrefix("packageName=") + ) + } + } + + constructor(id: Int = -1, scheme: String, host: String, packageName: String) : + this(id, scheme, listOf(host), packageName) + + fun serializeToString() = """ + id=$id + scheme=$scheme + hosts=${hosts.joinToString(separator = ",")} + packageName=$packageName + """.trimIndent() + "\n" +} diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt index 3847658def6a..e17358d38d8c 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt @@ -31,7 +31,8 @@ class FactoryPackageTest : BaseHostJUnit4Test() { private val preparer: SystemPreparer = SystemPreparer(tempFolder, SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device } - @get:Rule + @Rule + @JvmField val rules = RuleChain.outerRule(tempFolder).around(preparer)!! private val filePath = HostUtils.makePathForApk("PackageManagerTestApp.apk", Partition.SYSTEM) diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt index 8dfefaf9750f..24c714c0d5f2 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt @@ -18,6 +18,7 @@ package com.android.server.pm.test import com.android.internal.util.test.SystemPreparer import com.android.tradefed.device.ITestDevice +import org.junit.rules.TemporaryFolder import java.io.File import java.io.FileOutputStream @@ -34,6 +35,19 @@ internal fun SystemPreparer.deleteApkFolders( } } +internal fun ITestDevice.installJavaResourceApk( + tempFolder: TemporaryFolder, + javaResource: String, + reinstall: Boolean = true, + extraArgs: Array<String> = emptyArray() +): String? { + val file = HostUtils.copyResourceToHostFile(javaResource, tempFolder.newFile()) + return installPackage(file, reinstall, *extraArgs) +} + +internal fun ITestDevice.uninstallPackages(vararg pkgNames: String) = + pkgNames.forEach { uninstallPackage(it) } + internal object HostUtils { fun getDataDir(device: ITestDevice, pkgName: String) = diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt index b7d135991ccd..37c999cbee68 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt @@ -47,7 +47,8 @@ class InvalidNewSystemAppTest : BaseHostJUnit4Test() { private val preparer: SystemPreparer = SystemPreparer(tempFolder, SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device } - @get:Rule + @Rule + @JvmField val rules = RuleChain.outerRule(tempFolder).around(preparer)!! private val filePath = HostUtils.makePathForApk("PackageManagerTestApp.apk", Partition.PRODUCT) diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt index 4ae3ca5f7263..4becae66633f 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt @@ -47,7 +47,8 @@ class OriginalPackageMigrationTest : BaseHostJUnit4Test() { private val preparer: SystemPreparer = SystemPreparer(tempFolder, SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device } - @get:Rule + @Rule + @JvmField val rules = RuleChain.outerRule(tempFolder).around(preparer)!! @Before diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt index 654c11c5bf81..6479f584324f 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt @@ -22,6 +22,7 @@ import java.nio.file.Paths // Unfortunately no easy way to access PMS SystemPartitions, so mock them here internal enum class Partition(val baseAppFolder: Path) { SYSTEM("/system/app"), + SYSTEM_PRIVILEGED("/system/priv-app"), VENDOR("/vendor/app"), PRODUCT("/product/app"), SYSTEM_EXT("/system_ext/app") diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt index 207f10a3027b..46120af06550 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt @@ -110,7 +110,8 @@ class SystemStubMultiUserDisableUninstallTest : BaseHostJUnit4Test() { private val preparer: SystemPreparer = SystemPreparer(tempFolder, SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device } - @get:Rule + @Rule + @JvmField val rules = RuleChain.outerRule(tempFolder).let { if (DEBUG_NO_REBOOT) { it!! diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/intent/verify/IntentFilterVerificationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/intent/verify/IntentFilterVerificationTest.kt new file mode 100644 index 000000000000..fffda8ebd36c --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/intent/verify/IntentFilterVerificationTest.kt @@ -0,0 +1,413 @@ +/* + * Copyright (C) 2020 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 com.android.server.pm.test.intent.verify + +import com.android.internal.util.test.SystemPreparer +import com.android.server.pm.test.Partition +import com.android.server.pm.test.deleteApkFolders +import com.android.server.pm.test.installJavaResourceApk +import com.android.server.pm.test.pushApk +import com.android.server.pm.test.uninstallPackages +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Before +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith +import java.io.File +import java.util.concurrent.TimeUnit + +@RunWith(DeviceJUnit4ClassRunner::class) +class IntentFilterVerificationTest : BaseHostJUnit4Test() { + + companion object { + private const val VERIFIER = "PackageManagerTestIntentVerifier.apk" + private const val VERIFIER_PKG_NAME = "com.android.server.pm.test.intent.verifier" + private const val TARGET_PKG_PREFIX = "$VERIFIER_PKG_NAME.target" + private const val TARGET_APK_PREFIX = "PackageManagerTestIntentVerifierTarget" + private const val TARGET_ONE = "${TARGET_APK_PREFIX}1.apk" + private const val TARGET_ONE_PKG_NAME = "$TARGET_PKG_PREFIX.one" + private const val TARGET_TWO = "${TARGET_APK_PREFIX}2.apk" + private const val TARGET_TWO_PKG_NAME = "$TARGET_PKG_PREFIX.two" + private const val TARGET_THREE = "${TARGET_APK_PREFIX}3.apk" + private const val TARGET_THREE_PKG_NAME = "$TARGET_PKG_PREFIX.three" + private const val TARGET_FOUR_BASE = "${TARGET_APK_PREFIX}4Base.apk" + private const val TARGET_FOUR_PKG_NAME = "$TARGET_PKG_PREFIX.four" + private const val TARGET_FOUR_NO_AUTO_VERIFY = "${TARGET_APK_PREFIX}4NoAutoVerify.apk" + private const val TARGET_FOUR_WILDCARD = "${TARGET_APK_PREFIX}4Wildcard.apk" + private const val TARGET_FOUR_WILDCARD_NO_AUTO_VERIFY = + "${TARGET_APK_PREFIX}4WildcardNoAutoVerify.apk" + + @get:ClassRule + val deviceRebootRule = SystemPreparer.TestRuleDelegate(true) + } + + private val tempFolder = TemporaryFolder() + private val preparer: SystemPreparer = SystemPreparer(tempFolder, + SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device } + + @Rule + @JvmField + val rules = RuleChain.outerRule(tempFolder).around(preparer)!! + + private val permissionsFile = File("/system/etc/permissions" + + "/privapp-PackageManagerIntentFilterVerificationTest-permissions.xml") + + @Before + fun cleanupAndPushPermissionsFile() { + // In order for the test app to be the verification agent, it needs a permission file + // which can be pushed onto the system and removed afterwards. + val file = tempFolder.newFile().apply { + """ + <permissions> + <privapp-permissions package="$VERIFIER_PKG_NAME"> + <permission name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/> + </privapp-permissions> + </permissions> + """ + .trimIndent() + .let { writeText(it) } + } + device.uninstallPackages(TARGET_ONE_PKG_NAME, TARGET_TWO_PKG_NAME, TARGET_THREE_PKG_NAME, + TARGET_FOUR_PKG_NAME) + preparer.pushApk(VERIFIER, Partition.SYSTEM_PRIVILEGED) + .pushFile(file, permissionsFile.toString()) + .reboot() + runTest("clearResponse") + } + + @After + fun cleanupAndDeletePermissionsFile() { + device.uninstallPackages(TARGET_ONE_PKG_NAME, TARGET_TWO_PKG_NAME, TARGET_THREE_PKG_NAME, + TARGET_FOUR_PKG_NAME) + preparer.deleteApkFolders(Partition.SYSTEM_PRIVILEGED, VERIFIER) + .deleteFile(permissionsFile.toString()) + device.reboot() + } + + @Test + fun verifyOne() { + installPackage(TARGET_ONE) + + assertReceivedRequests(true, VerifyRequest( + scheme = "https", + hosts = listOf( + "https_only.pm.server.android.com", + "other_activity.pm.server.android.com", + "http_only.pm.server.android.com", + "verify.pm.server.android.com", + "https_plus_non_web_scheme.pm.server.android.com", + "multiple.pm.server.android.com", + // TODO(b/159952358): the following domain should not be + // verified, this is because the verifier tries to verify all web domains, + // even in intent filters not marked for auto verify + "no_verify.pm.server.android.com" + ), + packageName = TARGET_ONE_PKG_NAME + )) + + runTest(StartActivityParams( + uri = "https://https_only.pm.server.android.com", + expected = "$TARGET_ONE_PKG_NAME.TargetActivity" + )) + } + + @Test + fun nonWebScheme() { + installPackage(TARGET_TWO) + assertReceivedRequests(null) + } + + @Test + fun verifyHttpNonSecureOnly() { + installPackage(TARGET_THREE) + assertReceivedRequests(true, VerifyRequest( + scheme = "https", + hosts = listOf( + "multiple.pm.server.android.com" + ), + packageName = TARGET_THREE_PKG_NAME + )) + + runTest(StartActivityParams( + uri = "http://multiple.pm.server.android.com", + expected = "$TARGET_THREE_PKG_NAME.TargetActivity" + )) + } + + @Test + fun multipleResults() { + installPackage(TARGET_ONE) + installPackage(TARGET_THREE) + assertReceivedRequests(true, VerifyRequest( + scheme = "https", + hosts = listOf( + "https_only.pm.server.android.com", + "other_activity.pm.server.android.com", + "http_only.pm.server.android.com", + "verify.pm.server.android.com", + "https_plus_non_web_scheme.pm.server.android.com", + "multiple.pm.server.android.com", + // TODO(b/159952358): the following domain should not be + // verified, this is because the verifier tries to verify all web domains, + // even in intent filters not marked for auto verify + "no_verify.pm.server.android.com" + ), + packageName = TARGET_ONE_PKG_NAME + ), VerifyRequest( + scheme = "https", + hosts = listOf( + "multiple.pm.server.android.com" + ), + packageName = TARGET_THREE_PKG_NAME + )) + + // Target3 declares http non-s, so it should be included in the set here + runTest(StartActivityParams( + uri = "http://multiple.pm.server.android.com", + expected = listOf( + "$TARGET_ONE_PKG_NAME.TargetActivity2", + "$TARGET_THREE_PKG_NAME.TargetActivity" + ) + )) + + // But it excludes https, so it shouldn't resolve here + runTest(StartActivityParams( + uri = "https://multiple.pm.server.android.com", + expected = "$TARGET_ONE_PKG_NAME.TargetActivity2" + )) + + // Remove Target3 and return to single verified Target1 app for http non-s + device.uninstallPackage(TARGET_THREE_PKG_NAME) + runTest(StartActivityParams( + uri = "http://multiple.pm.server.android.com", + expected = "$TARGET_ONE_PKG_NAME.TargetActivity2" + )) + } + + @Test + fun demoteAlways() { + installPackage(TARGET_FOUR_BASE) + assertReceivedRequests(false, VerifyRequest( + scheme = "https", + host = "failing.pm.server.android.com", + packageName = TARGET_FOUR_PKG_NAME + )) + + runTest(StartActivityParams( + uri = "https://failing.pm.server.android.com", + expected = "$TARGET_FOUR_PKG_NAME.TargetActivity", + withBrowsers = true + )) + runTest(SetActivityAsAlwaysParams( + uri = "https://failing.pm.server.android.com", + packageName = TARGET_FOUR_PKG_NAME, + activityName = "$TARGET_FOUR_PKG_NAME.TargetActivity" + )) + runTest(StartActivityParams( + uri = "https://failing.pm.server.android.com", + expected = "$TARGET_FOUR_PKG_NAME.TargetActivity" + )) + + // Re-installing with same host/verify set will maintain always setting + installPackage(TARGET_FOUR_BASE) + assertReceivedRequests(null) + runTest(StartActivityParams( + uri = "https://failing.pm.server.android.com", + expected = "$TARGET_FOUR_PKG_NAME.TargetActivity" + )) + + // Installing with new wildcard host will downgrade out of always, re-including browsers + installPackage(TARGET_FOUR_WILDCARD) + + // TODO(b/159952358): The first request without the wildcard should not be sent. This is + // caused by the request being queued even if it should be dropped from the previous + // install case since the host set didn't change. + assertReceivedRequests(false, VerifyRequest( + scheme = "https", + hosts = listOf("failing.pm.server.android.com"), + packageName = TARGET_FOUR_PKG_NAME + ), VerifyRequest( + scheme = "https", + hosts = listOf("failing.pm.server.android.com", "wildcard.tld"), + packageName = TARGET_FOUR_PKG_NAME + )) + runTest(StartActivityParams( + uri = "https://failing.pm.server.android.com", + expected = "$TARGET_FOUR_PKG_NAME.TargetActivity", + withBrowsers = true + )) + } + + @Test + fun unverifiedReinstallResendRequest() { + installPackage(TARGET_FOUR_BASE) + assertReceivedRequests(false, VerifyRequest( + scheme = "https", + host = "failing.pm.server.android.com", + packageName = TARGET_FOUR_PKG_NAME + )) + + installPackage(TARGET_FOUR_BASE) + + assertReceivedRequests(false, VerifyRequest( + scheme = "https", + host = "failing.pm.server.android.com", + packageName = TARGET_FOUR_PKG_NAME + )) + } + + @Test + fun unverifiedUpdateRemovingDomainNoRequestDemoteAlways() { + installPackage(TARGET_FOUR_WILDCARD) + assertReceivedRequests(false, VerifyRequest( + scheme = "https", + hosts = listOf("failing.pm.server.android.com", "wildcard.tld"), + packageName = TARGET_FOUR_PKG_NAME + )) + + runTest(SetActivityAsAlwaysParams( + uri = "https://failing.pm.server.android.com", + packageName = TARGET_FOUR_PKG_NAME, + activityName = "$TARGET_FOUR_PKG_NAME.TargetActivity" + )) + + // Re-installing with a smaller host/verify set will not request re-verification + installPackage(TARGET_FOUR_BASE) + assertReceivedRequests(null) + runTest(StartActivityParams( + uri = "https://failing.pm.server.android.com", + expected = "$TARGET_FOUR_PKG_NAME.TargetActivity" + )) + + // Re-installing with a (now) larger host/verify set will re-request and demote + installPackage(TARGET_FOUR_WILDCARD) + // TODO(b/159952358): The first request should not be sent. This is caused by the request + // being queued even if it should be dropped from the previous install case. + assertReceivedRequests(false, VerifyRequest( + scheme = "https", + host = "failing.pm.server.android.com", + packageName = TARGET_FOUR_PKG_NAME + ), VerifyRequest( + scheme = "https", + hosts = listOf("failing.pm.server.android.com", "wildcard.tld"), + packageName = TARGET_FOUR_PKG_NAME + )) + + runTest(StartActivityParams( + uri = "https://failing.pm.server.android.com", + expected = "$TARGET_FOUR_PKG_NAME.TargetActivity", + withBrowsers = true + )) + } + + // TODO(b/159952358): I would expect this to demote + // TODO(b/32810168) + @Test + fun verifiedUpdateRemovingAutoVerifyMaintainsAlways() { + installPackage(TARGET_FOUR_BASE) + assertReceivedRequests(true, VerifyRequest( + scheme = "https", + host = "failing.pm.server.android.com", + packageName = TARGET_FOUR_PKG_NAME + )) + + runTest(StartActivityParams( + uri = "https://failing.pm.server.android.com", + expected = "$TARGET_FOUR_PKG_NAME.TargetActivity" + )) + + installPackage(TARGET_FOUR_NO_AUTO_VERIFY) + assertReceivedRequests(null) + + runTest(StartActivityParams( + uri = "https://failing.pm.server.android.com", + expected = "$TARGET_FOUR_PKG_NAME.TargetActivity" + )) + } + + @Test + fun verifiedUpdateRemovingAutoVerifyAddingDomainDemotesAlways() { + installPackage(TARGET_FOUR_BASE) + + assertReceivedRequests(true, VerifyRequest( + scheme = "https", + host = "failing.pm.server.android.com", + packageName = TARGET_FOUR_PKG_NAME + )) + + runTest(StartActivityParams( + uri = "https://failing.pm.server.android.com", + expected = "$TARGET_FOUR_PKG_NAME.TargetActivity" + )) + + installPackage(TARGET_FOUR_WILDCARD_NO_AUTO_VERIFY) + assertReceivedRequests(null) + + runTest(StartActivityParams( + uri = "https://failing.pm.server.android.com", + expected = "$TARGET_FOUR_PKG_NAME.TargetActivity", + withBrowsers = true + )) + } + + private fun installPackage(javaResourceName: String) { + // Need to pass --user as verification is not currently run for all user installs + assertThat(device.installJavaResourceApk(tempFolder, javaResourceName, + extraArgs = arrayOf("--user", device.currentUser.toString()))).isNull() + } + + private fun assertReceivedRequests(success: Boolean?, vararg expected: VerifyRequest?) { + // TODO(b/159952358): This can probably be less than 10 + // Because tests have to assert that multiple broadcasts aren't received, there's no real + // better way to await for a value than sleeping for a long enough time. + TimeUnit.SECONDS.sleep(10) + + val params = mutableMapOf<String, String>() + if (expected.any { it != null }) { + params["expected"] = expected.filterNotNull() + .joinToString(separator = "") { it.serializeToString() } + } + runTest("compareLastReceived", params) + + if (success != null) { + if (success) { + runTest("verifyPreviousReceivedSuccess") + } else { + runTest("verifyPreviousReceivedFailure") + } + runTest("clearResponse") + } + } + + private fun runTest(params: IntentVerifyTestParams) = + runTest(params.methodName, params.toArgsMap()) + + private fun runTest(testName: String, args: Map<String, String> = emptyMap()) { + val escapedArgs = args.mapValues { + // Need to escape strings so that args are passed properly through the shell command + "\"${it.value.trim('"')}\"" + } + runDeviceTests(device, null, VERIFIER_PKG_NAME, "$VERIFIER_PKG_NAME.VerifyReceiverTest", + testName, null, 10 * 60 * 1000L, 10 * 60 * 1000L, 0L, true, false, escapedArgs) + } +} diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp new file mode 100644 index 000000000000..e82f57d20fcc --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp @@ -0,0 +1,28 @@ +// Copyright (C) 2020 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. + +android_test_helper_app { + name: "PackageManagerTestIntentVerifier", + srcs: [ "src/**/*.kt" ], + static_libs: [ + "androidx.test.core", + "androidx.test.espresso.core", + "androidx.test.runner", + "compatibility-device-util-axt", + "junit", + "truth-prebuilt", + "PackageManagerServiceHostTestsIntentVerifyUtils", + ], + platform_apis: true, +} diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/AndroidManifest.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/AndroidManifest.xml new file mode 100644 index 000000000000..17b50b0ec949 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/AndroidManifest.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2020 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. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.pm.test.intent.verifier" + > + + <uses-permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT" /> + <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> + <uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS" /> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.pm.test.intent.verifier" + /> + + <application> + <receiver android:name=".VerifyReceiver" android:exported="true"> + <intent-filter android:priority="999"> + <action android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION"/> + <data android:mimeType="application/vnd.android.package-archive"/> + </intent-filter> + </receiver> + </application> + +</manifest> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiver.kt b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiver.kt new file mode 100644 index 000000000000..073c2be75424 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiver.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2020 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 com.android.server.pm.test.intent.verifier + +import android.content.BroadcastReceiver +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import com.android.server.pm.test.intent.verify.VerifyRequest + +class VerifyReceiver : BroadcastReceiver() { + + override fun onReceive(context: Context, intent: Intent) { + if (intent.action != Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION) return + val params = intent.toVerifyParams() + + // If the receiver is called for a normal request, proxy it to the real verifier on device + if (params.hosts.none { it.contains("pm.server.android.com") }) { + sendToRealVerifier(context, Intent(intent)) + return + } + + // When the receiver is invoked for a test install, there is no direct connection to host, + // so store the result in a file to read and assert on later. Append is intentional so that + // amount of invocations and clean up can be verified. + context.filesDir.resolve("test.txt") + .appendText(params.serializeToString()) + } + + private fun sendToRealVerifier(context: Context, intent: Intent) { + context.packageManager.queryBroadcastReceivers(intent, 0) + .first { it.activityInfo?.packageName != context.packageName } + .let { it.activityInfo!! } + .let { intent.setComponent(ComponentName(it.packageName, it.name)) } + .run { context.sendBroadcast(intent) } + } + + private fun Intent.toVerifyParams() = VerifyRequest( + id = getIntExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID, -1), + scheme = getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME)!!, + hosts = getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS)!! + .split(' '), + packageName = getStringExtra( + PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME)!! + + ) +} diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt new file mode 100644 index 000000000000..6de3d4e160ec --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2020 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 com.android.server.pm.test.intent.verifier + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Bundle +import android.os.UserHandle +import androidx.test.InstrumentationRegistry +import androidx.test.runner.AndroidJUnit4 +import com.android.compatibility.common.util.ShellIdentityUtils +import com.android.server.pm.test.intent.verify.SetActivityAsAlwaysParams +import com.android.server.pm.test.intent.verify.StartActivityParams +import com.android.server.pm.test.intent.verify.VerifyRequest +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import java.io.File + +@RunWith(AndroidJUnit4::class) +class VerifyReceiverTest { + + val args: Bundle = InstrumentationRegistry.getArguments() + val context: Context = InstrumentationRegistry.getContext() + + private val file = context.filesDir.resolve("test.txt") + + @Test + fun clearResponse() { + file.delete() + } + + @Test + fun compareLastReceived() { + val lastReceivedText = file.readTextIfExists() + val expectedText = args.getString("expected") + if (expectedText.isNullOrEmpty()) { + assertThat(lastReceivedText).isEmpty() + return + } + + val expectedParams = expectedText.parseParams() + val lastReceivedParams = lastReceivedText.parseParams() + + assertThat(lastReceivedParams).hasSize(expectedParams.size) + + lastReceivedParams.zip(expectedParams).forEach { (actual, expected) -> + assertThat(actual.hosts).containsExactlyElementsIn(expected.hosts) + assertThat(actual.packageName).isEqualTo(expected.packageName) + assertThat(actual.scheme).isEqualTo(expected.scheme) + } + } + + @Test + fun setActivityAsAlways() { + val params = SetActivityAsAlwaysParams.fromArgs( + args.keySet().associateWith { args.getString(it)!! }) + val uri = Uri.parse(params.uri) + val filter = IntentFilter().apply { + addAction(Intent.ACTION_VIEW) + addCategory(Intent.CATEGORY_DEFAULT) + addDataScheme(uri.scheme) + addDataAuthority(uri.authority, null) + } + + val intent = Intent(Intent.ACTION_VIEW, uri).apply { + addCategory(Intent.CATEGORY_DEFAULT) + } + val allResults = context.packageManager.queryIntentActivities(intent, 0) + val allComponents = allResults + .map { ComponentName(it.activityInfo.packageName, it.activityInfo.name) } + .toTypedArray() + val matchingInfo = allResults.first { + it.activityInfo.packageName == params.packageName && + it.activityInfo.name == params.activityName + } + + ShellIdentityUtils.invokeMethodWithShellPermissions(context.packageManager, + ShellIdentityUtils.ShellPermissionMethodHelper<Unit, PackageManager> { + it.addUniquePreferredActivity(filter, matchingInfo.match, allComponents, + ComponentName(matchingInfo.activityInfo.packageName, + matchingInfo.activityInfo.name)) + it.updateIntentVerificationStatusAsUser(params.packageName, + PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, + UserHandle.myUserId()) + }, "android.permission.SET_PREFERRED_APPLICATIONS") + } + + @Test + fun verifyPreviousReceivedSuccess() { + file.readTextIfExists() + .parseParams() + .forEach { + context.packageManager.verifyIntentFilter(it.id, + PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS, emptyList()) + } + } + + @Test + fun verifyPreviousReceivedFailure() { + file.readTextIfExists() + .parseParams() + .forEach { + context.packageManager.verifyIntentFilter(it.id, + PackageManager.INTENT_FILTER_VERIFICATION_FAILURE, it.hosts) + } + } + + @Test + fun verifyActivityStart() { + val params = StartActivityParams + .fromArgs(args.keySet().associateWith { args.getString(it)!! }) + val uri = Uri.parse(params.uri) + val intent = Intent(Intent.ACTION_VIEW).apply { + data = uri + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + + val expectedActivities = params.expected.toMutableList() + + if (params.withBrowsers) { + // Since the host doesn't know what browsers the device has, query here and add it to + // set if it's expected that browser are returned + val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://example.com")) + expectedActivities += context.packageManager.queryIntentActivities(browserIntent, 0) + .map { it.activityInfo.name } + } + + val infos = context.packageManager.queryIntentActivities(intent, 0) + .map { it.activityInfo.name } + assertThat(infos).containsExactlyElementsIn(expectedActivities) + } + + private fun File.readTextIfExists() = if (exists()) readText() else "" + + // Rudimentary list deserialization by splitting text block into 4 line sections + private fun String.parseParams() = trim() + .lines() + .windowed(4, 4) + .map { it.joinToString(separator = "\n") } + .map { VerifyRequest.deserialize(it) } +} diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp new file mode 100644 index 000000000000..7161fdd33516 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp @@ -0,0 +1,48 @@ +// Copyright (C) 2020 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. + +android_test_helper_app { + name: "PackageManagerTestIntentVerifierTarget1", + manifest: "AndroidManifest1.xml", +} + +android_test_helper_app { + name: "PackageManagerTestIntentVerifierTarget2", + manifest: "AndroidManifest2.xml", +} + +android_test_helper_app { + name: "PackageManagerTestIntentVerifierTarget3", + manifest: "AndroidManifest3.xml", +} + +android_test_helper_app { + name: "PackageManagerTestIntentVerifierTarget4Base", + manifest: "AndroidManifest4Base.xml", +} + +android_test_helper_app { + name: "PackageManagerTestIntentVerifierTarget4NoAutoVerify", + manifest: "AndroidManifest4NoAutoVerify.xml", +} + +android_test_helper_app { + name: "PackageManagerTestIntentVerifierTarget4Wildcard", + manifest: "AndroidManifest4Wildcard.xml", +} + +android_test_helper_app { + name: "PackageManagerTestIntentVerifierTarget4WildcardNoAutoVerify", + manifest: "AndroidManifest4WildcardNoAutoVerify.xml", +} diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest1.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest1.xml new file mode 100644 index 000000000000..6cf5c7619a30 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest1.xml @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2020 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. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.pm.test.intent.verifier.target.one" android:versionCode="1"> + + <application> + <activity android:name=".TargetActivity" android:exported="true"> + <intent-filter android:autoVerify="true"> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="http" /> + <data android:scheme="https" /> + <data android:host="verify.pm.server.android.com" /> + </intent-filter> + + <intent-filter android:autoVerify="false"> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="http" /> + <data android:scheme="https" /> + <data android:host="no_verify.pm.server.android.com" /> + </intent-filter> + + <intent-filter android:autoVerify="true"> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="http" /> + <data android:host="http_only.pm.server.android.com" /> + </intent-filter> + + <intent-filter android:autoVerify="true"> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="https" /> + <data android:host="https_only.pm.server.android.com" /> + </intent-filter> + + <intent-filter android:autoVerify="true"> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="htttps" /> + <data android:host="non_http.pm.server.android.com" /> + </intent-filter> + + <intent-filter android:autoVerify="true"> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="https" /> + <data android:scheme="non_web_scheme" /> + <data android:host="https_plus_non_web_scheme.pm.server.android.com" /> + </intent-filter> + </activity> + + <activity android:name=".TargetActivity2" android:exported="true"> + <intent-filter android:autoVerify="true"> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="http" /> + <data android:scheme="https" /> + <data android:host="other_activity.pm.server.android.com" /> + </intent-filter> + + <intent-filter android:autoVerify="true"> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="http" /> + <data android:scheme="https" /> + <data android:host="multiple.pm.server.android.com" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest2.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest2.xml new file mode 100644 index 000000000000..087ef70595f9 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest2.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2020 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. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.pm.test.intent.verifier.target.two" + android:versionCode="1"> + + <application> + <activity android:name=".TargetActivity" android:exported="true"> + <intent-filter android:autoVerify="true"> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="http" /> + <data android:scheme="https" /> + <data android:scheme="non_web_scheme" /> + <data android:host="only_https_plus_non_web_scheme.pm.server.android.com" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest3.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest3.xml new file mode 100644 index 000000000000..eb75b5e53bc8 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest3.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2020 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. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.pm.test.intent.verifier.target.three" + android:versionCode="1"> + + <application> + <activity android:name=".TargetActivity" android:exported="true"> + <intent-filter android:autoVerify="true"> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="http" /> + <data android:host="multiple.pm.server.android.com" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Base.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Base.xml new file mode 100644 index 000000000000..7eacb8bc8fb7 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Base.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2020 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. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.pm.test.intent.verifier.target.four" + android:versionCode="1"> + + <application> + <activity android:name=".TargetActivity" android:exported="true"> + <intent-filter android:autoVerify="true"> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="http" /> + <data android:scheme="https" /> + <data android:host="failing.pm.server.android.com" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4NoAutoVerify.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4NoAutoVerify.xml new file mode 100644 index 000000000000..ecfee55b9c4a --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4NoAutoVerify.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2020 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. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.pm.test.intent.verifier.target.four" + android:versionCode="1"> + + <application> + <activity android:name=".TargetActivity" android:exported="true"> + <intent-filter android:autoVerify="false"> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="http" /> + <data android:scheme="https" /> + <data android:host="failing.pm.server.android.com" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Wildcard.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Wildcard.xml new file mode 100644 index 000000000000..0f0f53ba07e9 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Wildcard.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2020 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. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.pm.test.intent.verifier.target.four" + android:versionCode="1"> + + <application> + <activity android:name=".TargetActivity" android:exported="true"> + <intent-filter android:autoVerify="true"> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="http" /> + <data android:scheme="https" /> + <data android:host="failing.pm.server.android.com" /> + <data android:host="*.wildcard.tld" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4WildcardNoAutoVerify.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4WildcardNoAutoVerify.xml new file mode 100644 index 000000000000..d5652e1b924d --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4WildcardNoAutoVerify.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2020 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. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.pm.test.intent.verifier.target.four" + android:versionCode="1"> + + <application> + <activity android:name=".TargetActivity" android:exported="true"> + <intent-filter android:autoVerify="false"> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="http" /> + <data android:scheme="https" /> + <data android:host="failing.pm.server.android.com" /> + <data android:host="*.wildcard.tld" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java index 2f5e8839504b..3220dff553d3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java @@ -48,6 +48,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.net.Uri; import android.os.BatteryManager; import android.os.Handler; import android.os.Looper; @@ -282,7 +283,7 @@ public class AppStateTrackerTest { eq(ServiceType.FORCE_ALL_APPS_STANDBY), powerSaveObserverCaptor.capture()); - verify(mMockContext).registerReceiver( + verify(mMockContext, times(2)).registerReceiver( receiverCaptor.capture(), any(IntentFilter.class)); verify(mMockAppStandbyInternal).addListener( appIdleStateChangeListenerCaptor.capture()); @@ -1242,6 +1243,67 @@ public class AppStateTrackerTest { assertTrue(instance.isForceAllAppsStandbyEnabled()); } + @Test + public void testStateClearedOnPackageRemoved() throws Exception { + final AppStateTrackerTestable instance = newInstance(); + callStart(instance); + + instance.mActiveUids.put(UID_1, true); + instance.mForegroundUids.put(UID_2, true); + instance.mRunAnyRestrictedPackages.add(Pair.create(UID_1, PACKAGE_1)); + instance.mExemptedBucketPackages.add(UserHandle.getUserId(UID_2), PACKAGE_2); + + // Replace PACKAGE_1, nothing should change + Intent packageRemoved = new Intent(Intent.ACTION_PACKAGE_REMOVED) + .putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(UID_1)) + .putExtra(Intent.EXTRA_UID, UID_1) + .putExtra(Intent.EXTRA_REPLACING, true) + .setData(Uri.fromParts(IntentFilter.SCHEME_PACKAGE, PACKAGE_1, null)); + mReceiver.onReceive(mMockContext, packageRemoved); + + assertEquals(1, instance.mActiveUids.size()); + assertEquals(1, instance.mForegroundUids.size()); + assertEquals(1, instance.mRunAnyRestrictedPackages.size()); + assertEquals(1, instance.mExemptedBucketPackages.size()); + + // Replace PACKAGE_2, nothing should change + packageRemoved = new Intent(Intent.ACTION_PACKAGE_REMOVED) + .putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(UID_2)) + .putExtra(Intent.EXTRA_UID, UID_2) + .putExtra(Intent.EXTRA_REPLACING, true) + .setData(Uri.fromParts(IntentFilter.SCHEME_PACKAGE, PACKAGE_2, null)); + mReceiver.onReceive(mMockContext, packageRemoved); + + assertEquals(1, instance.mActiveUids.size()); + assertEquals(1, instance.mForegroundUids.size()); + assertEquals(1, instance.mRunAnyRestrictedPackages.size()); + assertEquals(1, instance.mExemptedBucketPackages.size()); + + // Remove PACKAGE_1 + packageRemoved = new Intent(Intent.ACTION_PACKAGE_REMOVED) + .putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(UID_1)) + .putExtra(Intent.EXTRA_UID, UID_1) + .setData(Uri.fromParts(IntentFilter.SCHEME_PACKAGE, PACKAGE_1, null)); + mReceiver.onReceive(mMockContext, packageRemoved); + + assertEquals(0, instance.mActiveUids.size()); + assertEquals(1, instance.mForegroundUids.size()); + assertEquals(0, instance.mRunAnyRestrictedPackages.size()); + assertEquals(1, instance.mExemptedBucketPackages.size()); + + // Remove PACKAGE_2 + packageRemoved = new Intent(Intent.ACTION_PACKAGE_REMOVED) + .putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(UID_2)) + .putExtra(Intent.EXTRA_UID, UID_2) + .setData(Uri.fromParts(IntentFilter.SCHEME_PACKAGE, PACKAGE_2, null)); + mReceiver.onReceive(mMockContext, packageRemoved); + + assertEquals(0, instance.mActiveUids.size()); + assertEquals(0, instance.mForegroundUids.size()); + assertEquals(0, instance.mRunAnyRestrictedPackages.size()); + assertEquals(0, instance.mExemptedBucketPackages.size()); + } + static int[] array(int... appIds) { Arrays.sort(appIds); return appIds; diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java index 7d6d90c4578c..b7a36f2eaed2 100644 --- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java @@ -21,7 +21,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; @@ -224,9 +223,24 @@ public class VibratorServiceTest { } @Test - public void arePrimitivesSupported_withComposeCapability_returnsAlwaysTrue() { + public void arePrimitivesSupported_withNullResultFromNative_returnsAlwaysFalse() { mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); - assertArrayEquals(new boolean[]{true, true}, + when(mNativeWrapperMock.vibratorGetSupportedPrimitives()).thenReturn(null); + + assertArrayEquals(new boolean[]{false, false}, + createService().arePrimitivesSupported(new int[]{ + VibrationEffect.Composition.PRIMITIVE_CLICK, + VibrationEffect.Composition.PRIMITIVE_QUICK_RISE + })); + } + + @Test + public void arePrimitivesSupported_withSomeSupportedPrimitives_returnsBasedOnNativeResult() { + mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + when(mNativeWrapperMock.vibratorGetSupportedPrimitives()) + .thenReturn(new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}); + + assertArrayEquals(new boolean[]{true, false}, createService().arePrimitivesSupported(new int[]{ VibrationEffect.Composition.PRIMITIVE_CLICK, VibrationEffect.Composition.PRIMITIVE_QUICK_RISE @@ -310,7 +324,7 @@ public class VibratorServiceTest { verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), - any(VibratorService.Vibration.class), eq(false)); + any(VibratorService.Vibration.class)); } @Test @@ -387,21 +401,26 @@ public class VibratorServiceTest { } @Test - public void vibrate_withOneShotAndNativeCallbackNotTriggered_finishesVibrationViaFallback() { + public void vibrate_withPrebakedAndNativeCallbackTriggered_finishesVibration() { + when(mNativeWrapperMock.vibratorGetSupportedEffects()) + .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK}); + doAnswer(invocation -> { + ((VibratorService.Vibration) invocation.getArgument(2)).onComplete(); + return 10_000L; // 10s + }).when(mNativeWrapperMock).vibratorPerformEffect( + anyLong(), anyLong(), any(VibratorService.Vibration.class)); VibratorService service = createService(); Mockito.clearInvocations(mNativeWrapperMock); - vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); - - verify(mNativeWrapperMock).vibratorOff(); - verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class)); - Mockito.clearInvocations(mNativeWrapperMock); - - // Run the scheduled callback to finish one-shot vibration. - mTestLooper.moveTimeForward(200); - mTestLooper.dispatchAll(); + vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); - verify(mNativeWrapperMock).vibratorOff(); + InOrder inOrderVerifier = inOrder(mNativeWrapperMock); + inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); + inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformEffect( + eq((long) VibrationEffect.EFFECT_CLICK), + eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), + any(VibratorService.Vibration.class)); + inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); } @Test @@ -447,30 +466,6 @@ public class VibratorServiceTest { } @Test - public void vibrate_withComposedAndNativeCallbackNotTriggered_finishesVibrationViaFallback() { - mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); - VibratorService service = createService(); - Mockito.clearInvocations(mNativeWrapperMock); - - VibrationEffect effect = VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10) - .compose(); - vibrate(service, effect); - - verify(mNativeWrapperMock).vibratorOff(); - verify(mNativeWrapperMock).vibratorPerformComposedEffect( - any(VibrationEffect.Composition.PrimitiveEffect[].class), - any(VibratorService.Vibration.class)); - Mockito.clearInvocations(mNativeWrapperMock); - - // Run the scheduled callback to finish one-shot vibration. - mTestLooper.moveTimeForward(10000); // 10s - mTestLooper.dispatchAll(); - - verify(mNativeWrapperMock).vibratorOff(); - } - - @Test public void vibrate_whenBinderDies_cancelsVibration() { mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); doAnswer(invocation -> { @@ -574,15 +569,15 @@ public class VibratorServiceTest { verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_CLICK), - eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any(), anyBoolean()); + eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any()); verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_TICK), - eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), any(), anyBoolean()); + eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), any()); verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_DOUBLE_CLICK), - eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), any(), anyBoolean()); + eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), any()); verify(mNativeWrapperMock, never()).vibratorPerformEffect( - eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), any(), anyBoolean()); + eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), any()); } @Test diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java index c29c510b35b5..42ba842f8434 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java @@ -17,23 +17,33 @@ package com.android.server.accessibility.magnification; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.view.Display; import android.view.accessibility.IWindowMagnificationConnection; import android.view.accessibility.IWindowMagnificationConnectionCallback; +/** + * Mocks the basic logic of window magnification in System UI. We assume the screen size is + * unlimited, so source bounds is always on the center of the mirror window bounds. + */ class MockWindowMagnificationConnection { + public static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY; private final IWindowMagnificationConnection mConnection; private final Binder mBinder; private IBinder.DeathRecipient mDeathRecipient; private IWindowMagnificationConnectionCallback mIMirrorWindowCallback; + private Rect mMirrorWindowFrame = new Rect(0, 0, 500, 500); MockWindowMagnificationConnection() throws RemoteException { mConnection = mock(IWindowMagnificationConnection.class); @@ -50,6 +60,30 @@ class MockWindowMagnificationConnection { return null; }).when(mBinder).linkToDeath( any(IBinder.DeathRecipient.class), eq(0)); + stubConnection(); + } + + private void stubConnection() throws RemoteException { + doAnswer((invocation) -> { + final int displayId = invocation.getArgument(0); + if (displayId != TEST_DISPLAY) { + throw new IllegalArgumentException("only support default display :" + displayId); + } + computeMirrorWindowFrame(invocation.getArgument(1), invocation.getArgument(2)); + + mIMirrorWindowCallback.onWindowMagnifierBoundsChanged(TEST_DISPLAY, + mMirrorWindowFrame); + return null; + }).when(mConnection).enableWindowMagnification(anyInt(), + anyFloat(), anyFloat(), anyFloat()); + } + + private void computeMirrorWindowFrame(float centerX, float centerY) { + final float offsetX = Float.isNaN(centerX) ? 0 + : centerX - mMirrorWindowFrame.exactCenterX(); + final float offsetY = Float.isNaN(centerY) ? 0 + : centerY - mMirrorWindowFrame.exactCenterY(); + mMirrorWindowFrame.offset((int) offsetX, (int) offsetY); } IWindowMagnificationConnection getConnection() { @@ -60,12 +94,16 @@ class MockWindowMagnificationConnection { return mBinder; } - public IBinder.DeathRecipient getDeathRecipient() { + IBinder.DeathRecipient getDeathRecipient() { return mDeathRecipient; } - public IWindowMagnificationConnectionCallback getConnectionCallback() { + IWindowMagnificationConnectionCallback getConnectionCallback() { return mIMirrorWindowCallback; } + + public Rect getMirrorWindowFrame() { + return new Rect(mMirrorWindowFrame); + } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java index 2b1bdc59d9c8..ed8dc4e470de 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java @@ -87,15 +87,15 @@ public class TwoFingersDownTest { secondPointerCoords.x = DEFAULT_X + 10; secondPointerCoords.y = DEFAULT_Y + 10; - final MotionEvent pointerDownEvent = TouchEventGenerator.pointerDownEvent( + final MotionEvent twoPointersDownEvent = TouchEventGenerator.twoPointersDownEvent( Display.DEFAULT_DISPLAY, defPointerCoords, secondPointerCoords); mGesturesObserver.onMotionEvent(downEvent, downEvent, 0); - mGesturesObserver.onMotionEvent(pointerDownEvent, pointerDownEvent, 0); + mGesturesObserver.onMotionEvent(twoPointersDownEvent, twoPointersDownEvent, 0); verify(mListener, timeout(sTimeoutMillis)).onGestureCompleted( - MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN, pointerDownEvent, - pointerDownEvent, 0); + MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN, twoPointersDownEvent, + twoPointersDownEvent, 0); } @Test diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java index e580340a29f7..bec9f26672f4 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java @@ -16,14 +16,9 @@ package com.android.server.accessibility.magnification; -import static android.view.MotionEvent.ACTION_POINTER_DOWN; - import static com.android.server.testutils.TestUtils.strictMock; import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.anyFloat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import android.content.Context; @@ -63,11 +58,9 @@ public class WindowMagnificationGestureHandlerTest { public static final int LAST_STATE = STATE_SHOW_MAGNIFIER_TRIPLE_TAP; // Co-prime x and y, to potentially catch x-y-swapped errors - public static final float DEFAULT_X = 301; - public static final float DEFAULT_Y = 299; - //Assume first pointer position (DEFAULT_X,DEFAULT_Y) is in the window. - public static Rect DEFAULT_WINDOW_FRAME = new Rect(0, 0, 500, 500); - private static final int DISPLAY_0 = 0; + public static final float DEFAULT_TAP_X = 301; + public static final float DEFAULT_TAP_Y = 299; + private static final int DISPLAY_0 = MockWindowMagnificationConnection.TEST_DISPLAY; private Context mContext; private WindowMagnificationManager mWindowMagnificationManager; @@ -83,14 +76,6 @@ public class WindowMagnificationGestureHandlerTest { mContext, mWindowMagnificationManager, mock(ScaleChangedListener.class), /** detectTripleTap= */true, /** detectShortcutTrigger= */true, DISPLAY_0); mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(DISPLAY_0, - DEFAULT_WINDOW_FRAME); - doAnswer((invocation) -> { - mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(DISPLAY_0, - DEFAULT_WINDOW_FRAME); - return null; - }).when(mMockConnection.getConnection()).enableWindowMagnification(eq(DISPLAY_0), - anyFloat(), anyFloat(), anyFloat()); mWindowMagnificationGestureHandler.setNext(strictMock(EventStreamTransformation.class)); } @@ -208,10 +193,11 @@ public class WindowMagnificationGestureHandlerTest { break; case STATE_TWO_FINGERS_DOWN: { goFromStateIdleTo(STATE_SHOW_MAGNIFIER); - send(downEvent()); + final Rect frame = mMockConnection.getMirrorWindowFrame(); + send(downEvent(frame.centerX(), frame.centerY())); //Second finger is outside the window. - send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_WINDOW_FRAME.right + 10, - DEFAULT_WINDOW_FRAME.bottom + 10)); + send(twoPointerDownEvent(new float[]{frame.centerX(), frame.centerX() + 10}, + new float[]{frame.centerY(), frame.centerY() + 10})); } break; case STATE_SHOW_MAGNIFIER_TRIPLE_TAP: { @@ -243,7 +229,8 @@ public class WindowMagnificationGestureHandlerTest { } break; case STATE_TWO_FINGERS_DOWN: { - send(upEvent()); + final Rect frame = mMockConnection.getMirrorWindowFrame(); + send(upEvent(frame.centerX(), frame.centerY())); returnToNormalFrom(STATE_SHOW_MAGNIFIER); } break; @@ -286,12 +273,8 @@ public class WindowMagnificationGestureHandlerTest { } } - private MotionEvent downEvent() { - return TouchEventGenerator.downEvent(DISPLAY_0, DEFAULT_X, DEFAULT_Y); - } - - private MotionEvent upEvent() { - return upEvent(DEFAULT_X, DEFAULT_Y); + private MotionEvent downEvent(float x, float y) { + return TouchEventGenerator.downEvent(DISPLAY_0, x, y); } private MotionEvent upEvent(float x, float y) { @@ -299,18 +282,18 @@ public class WindowMagnificationGestureHandlerTest { } private void tap() { - send(downEvent()); - send(upEvent()); + send(downEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y)); + send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y)); } - private MotionEvent pointerEvent(int action, float x, float y) { + private MotionEvent twoPointerDownEvent(float[] x, float[] y) { final MotionEvent.PointerCoords defPointerCoords = new MotionEvent.PointerCoords(); - defPointerCoords.x = DEFAULT_X; - defPointerCoords.y = DEFAULT_Y; + defPointerCoords.x = x[0]; + defPointerCoords.y = y[0]; final MotionEvent.PointerCoords pointerCoords = new MotionEvent.PointerCoords(); - pointerCoords.x = x; - pointerCoords.y = y; - return TouchEventGenerator.pointerDownEvent(DISPLAY_0, defPointerCoords, pointerCoords); + pointerCoords.x = x[1]; + pointerCoords.y = y[1]; + return TouchEventGenerator.twoPointersDownEvent(DISPLAY_0, defPointerCoords, pointerCoords); } private String stateDump() { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java b/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java index 7cbf3ee46594..a05881f78892 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java @@ -43,9 +43,9 @@ public class TouchEventGenerator { return generateSingleTouchEvent(displayId, ACTION_UP, x, y); } - public static MotionEvent pointerDownEvent(int displayId, PointerCoords defPointerCoords, + public static MotionEvent twoPointersDownEvent(int displayId, PointerCoords defPointerCoords, PointerCoords pointerCoords) { - return generatePointerEvent(displayId, ACTION_POINTER_DOWN, defPointerCoords, + return generateTwoPointersEvent(displayId, ACTION_POINTER_DOWN, defPointerCoords, pointerCoords); } @@ -59,7 +59,7 @@ public class TouchEventGenerator { return ev; } - private static MotionEvent generatePointerEvent(int displayId, int action, + private static MotionEvent generateTwoPointersEvent(int displayId, int action, PointerCoords defPointerCoords, PointerCoords pointerCoords) { final long downTime = SystemClock.uptimeMillis(); MotionEvent.PointerProperties defPointerProperties = new MotionEvent.PointerProperties(); diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java index 6a797f31fa55..03dce4c62fe8 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java @@ -32,7 +32,6 @@ import static android.util.DebugUtils.valueToString; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static com.android.server.am.ActivityManagerInternalTest.CustomThread; -import static com.android.server.am.ActivityManagerService.DISPATCH_UIDS_CHANGED_UI_MSG; import static com.android.server.am.ActivityManagerService.Injector; import static com.android.server.am.ProcessList.NETWORK_STATE_BLOCK; import static com.android.server.am.ProcessList.NETWORK_STATE_NO_CHANGE; @@ -548,10 +547,10 @@ public class ActivityManagerServiceTest { pendingChange.processState = procStatesForPendingUidRecords[i]; pendingChange.procStateSeq = i; changeItems.put(changesForPendingUidRecords[i], pendingChange); - mAms.mPendingUidChanges.add(pendingChange); + mAms.mUidObserverController.mPendingUidChanges.add(pendingChange); } - mAms.dispatchUidsChanged(); + mAms.mUidObserverController.dispatchUidsChanged(); // Verify the required changes have been dispatched to observers. for (int i = 0; i < observers.length; ++i) { final int changeToObserve = changesToObserve[i]; @@ -647,8 +646,8 @@ public class ActivityManagerServiceTest { changeItem.change = UidRecord.CHANGE_PROCSTATE; changeItem.processState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY; changeItem.procStateSeq = 111; - mAms.mPendingUidChanges.add(changeItem); - mAms.dispatchUidsChanged(); + mAms.mUidObserverController.mPendingUidChanges.add(changeItem); + mAms.mUidObserverController.dispatchUidsChanged(); // First process state message is always delivered regardless of whether the process state // change is above or below the cutpoint (PROCESS_STATE_SERVICE). verify(observer).onUidStateChanged(TEST_UID, @@ -657,15 +656,15 @@ public class ActivityManagerServiceTest { verifyNoMoreInteractions(observer); changeItem.processState = ActivityManager.PROCESS_STATE_RECEIVER; - mAms.mPendingUidChanges.add(changeItem); - mAms.dispatchUidsChanged(); + mAms.mUidObserverController.mPendingUidChanges.add(changeItem); + mAms.mUidObserverController.dispatchUidsChanged(); // Previous process state change is below cutpoint (PROCESS_STATE_SERVICE) and // the current process state change is also below cutpoint, so no callback will be invoked. verifyNoMoreInteractions(observer); changeItem.processState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; - mAms.mPendingUidChanges.add(changeItem); - mAms.dispatchUidsChanged(); + mAms.mUidObserverController.mPendingUidChanges.add(changeItem); + mAms.mUidObserverController.dispatchUidsChanged(); // Previous process state change is below cutpoint (PROCESS_STATE_SERVICE) and // the current process state change is above cutpoint, so callback will be invoked with the // current process state change. @@ -675,15 +674,15 @@ public class ActivityManagerServiceTest { verifyNoMoreInteractions(observer); changeItem.processState = ActivityManager.PROCESS_STATE_TOP; - mAms.mPendingUidChanges.add(changeItem); - mAms.dispatchUidsChanged(); + mAms.mUidObserverController.mPendingUidChanges.add(changeItem); + mAms.mUidObserverController.dispatchUidsChanged(); // Previous process state change is above cutpoint (PROCESS_STATE_SERVICE) and // the current process state change is also above cutpoint, so no callback will be invoked. verifyNoMoreInteractions(observer); changeItem.processState = ActivityManager.PROCESS_STATE_CACHED_EMPTY; - mAms.mPendingUidChanges.add(changeItem); - mAms.dispatchUidsChanged(); + mAms.mUidObserverController.mPendingUidChanges.add(changeItem); + mAms.mUidObserverController.dispatchUidsChanged(); // Previous process state change is above cutpoint (PROCESS_STATE_SERVICE) and // the current process state change is below cutpoint, so callback will be invoked with the // current process state change. @@ -720,20 +719,21 @@ public class ActivityManagerServiceTest { // Verify that when there no observers listening to uid state changes, then there will // be no changes to validateUids. - mAms.mPendingUidChanges.addAll(pendingItemsForUids); - mAms.dispatchUidsChanged(); + mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids); + mAms.mUidObserverController.dispatchUidsChanged(); assertEquals("No observers registered, so validateUids should be empty", - 0, mAms.mValidateUids.size()); + 0, mAms.mUidObserverController.mValidateUids.size()); final IUidObserver observer = mock(IUidObserver.Stub.class); when(observer.asBinder()).thenReturn((IBinder) observer); mAms.registerUidObserver(observer, 0, 0, null); // Verify that when observers are registered, then validateUids is correctly updated. - mAms.mPendingUidChanges.addAll(pendingItemsForUids); - mAms.dispatchUidsChanged(); + mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids); + mAms.mUidObserverController.dispatchUidsChanged(); for (int i = 0; i < pendingItemsForUids.size(); ++i) { final UidRecord.ChangeItem item = pendingItemsForUids.get(i); - final UidRecord validateUidRecord = mAms.mValidateUids.get(item.uid); + final UidRecord validateUidRecord = + mAms.mUidObserverController.mValidateUids.get(item.uid); if ((item.change & UidRecord.CHANGE_GONE) != 0) { assertNull("validateUidRecord should be null since the change is either " + "CHANGE_GONE or CHANGE_GONE_IDLE", validateUidRecord); @@ -759,7 +759,7 @@ public class ActivityManagerServiceTest { // Verify that when uid state changes to CHANGE_GONE or CHANGE_GONE_IDLE, then it // will be removed from validateUids. assertNotEquals("validateUids should not be empty", 0, - mAms.mValidateUids.size()); + mAms.mUidObserverController.mValidateUids.size()); for (int i = 0; i < pendingItemsForUids.size(); ++i) { final UidRecord.ChangeItem item = pendingItemsForUids.get(i); // Assign CHANGE_GONE_IDLE to some items and CHANGE_GONE to the others, using even/odd @@ -767,10 +767,11 @@ public class ActivityManagerServiceTest { item.change = (i % 2) == 0 ? (UidRecord.CHANGE_GONE | UidRecord.CHANGE_IDLE) : UidRecord.CHANGE_GONE; } - mAms.mPendingUidChanges.addAll(pendingItemsForUids); - mAms.dispatchUidsChanged(); - assertEquals("validateUids should be empty, size=" + mAms.mValidateUids.size(), - 0, mAms.mValidateUids.size()); + mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids); + mAms.mUidObserverController.dispatchUidsChanged(); + assertEquals("validateUids should be empty, size=" + + mAms.mUidObserverController.mValidateUids.size(), + 0, mAms.mUidObserverController.mValidateUids.size()); } @Test @@ -792,7 +793,7 @@ public class ActivityManagerServiceTest { @Test public void testEnqueueUidChangeLocked_nullUidRecord() { // Use "null" uidRecord to make sure there is no crash. - mAms.enqueueUidChangeLocked(null, TEST_UID, UidRecord.CHANGE_ACTIVE); + mAms.mUidObserverController.enqueueUidChangeLocked(null, TEST_UID, UidRecord.CHANGE_ACTIVE); } private void verifyLastProcStateSeqUpdated(UidRecord uidRecord, int uid, long curProcstateSeq) { @@ -801,7 +802,7 @@ public class ActivityManagerServiceTest { final int changeToDispatch = UID_RECORD_CHANGES[i]; // Reset lastProcStateSeqDispatchToObservers after every test. uidRecord.lastDispatchedProcStateSeq = 0; - mAms.enqueueUidChangeLocked(uidRecord, uid, changeToDispatch); + mAms.mUidObserverController.enqueueUidChangeLocked(uidRecord, uid, changeToDispatch); // Verify there is no effect on curProcStateSeq. assertEquals(curProcstateSeq, uidRecord.curProcStateSeq); if ((changeToDispatch & UidRecord.CHANGE_GONE) != 0) { @@ -833,9 +834,9 @@ public class ActivityManagerServiceTest { // Reset the current state mHandler.reset(); uidRecord.pendingChange = null; - mAms.mPendingUidChanges.clear(); + mAms.mUidObserverController.mPendingUidChanges.clear(); - mAms.enqueueUidChangeLocked(uidRecord, -1, changeToDispatch); + mAms.mUidObserverController.enqueueUidChangeLocked(uidRecord, -1, changeToDispatch); // Verify that UidRecord.pendingChange is updated correctly. assertNotNull(uidRecord.pendingChange); @@ -843,8 +844,7 @@ public class ActivityManagerServiceTest { assertEquals(expectedProcState, uidRecord.pendingChange.processState); assertEquals(TEST_PROC_STATE_SEQ1, uidRecord.pendingChange.procStateSeq); - // Verify that DISPATCH_UIDS_CHANGED_UI_MSG is posted to handler. - mHandler.waitForMessage(DISPATCH_UIDS_CHANGED_UI_MSG); + // TODO: Verify that DISPATCH_UIDS_CHANGED_UI_MSG is posted to handler. } } diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java index a9cef20268f4..609af8d5bf4d 100644 --- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java +++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java @@ -21,6 +21,8 @@ import android.media.AudioDeviceAttributes; import android.media.AudioSystem; import android.util.Log; +import java.util.List; + /** * Provides an adapter for AudioSystem that does nothing. * Overridden methods can be configured. @@ -66,13 +68,13 @@ public class NoOpAudioSystemAdapter extends AudioSystemAdapter { } @Override - public int setPreferredDeviceForStrategy(int strategy, - @NonNull AudioDeviceAttributes device) { + public int setDevicesRoleForStrategy(int strategy, int role, + @NonNull List<AudioDeviceAttributes> devices) { return AudioSystem.AUDIO_STATUS_OK; } @Override - public int removePreferredDeviceForStrategy(int strategy) { + public int removeDevicesRoleForStrategy(int strategy, int role) { return AudioSystem.AUDIO_STATUS_OK; } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java index a5df53205a36..94cad1ed18b8 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java @@ -269,21 +269,6 @@ public class AuthServiceTest { eq(callback), eq(UserHandle.getCallingUserId())); } - @Test - public void testResetLockout_callsBiometricServiceResetLockout() throws - Exception { - mAuthService = new AuthService(mContext, mInjector); - mAuthService.onStart(); - - final int userId = 100; - final byte[] token = new byte[0]; - - mAuthService.mImpl.resetLockout(userId, token); - - waitForIdle(); - verify(mBiometricService).resetLockout(eq(userId), AdditionalMatchers.aryEq(token)); - } - private static void waitForIdle() { InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index dad360d40515..36c55cce076b 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -17,7 +17,6 @@ package com.android.server.biometrics.sensors; import android.content.Context; -import android.os.IBinder; import android.platform.test.annotations.Presubmit; import androidx.annotation.NonNull; @@ -56,8 +55,8 @@ public class BiometricSchedulerTest { mScheduler.scheduleClientMonitor(client1); mScheduler.scheduleClientMonitor(client2); - client1.mFinishCallback.onClientFinished(client1, true /* success */); - client1.mFinishCallback.onClientFinished(client1, true /* success */); + client1.mCallback.onClientFinished(client1, true /* success */); + client1.mCallback.onClientFinished(client1, true /* success */); } private static class TestClientMonitor extends ClientMonitor<Object> { diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 8fc228734f37..32afe8244eb6 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -149,6 +149,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { public static final String NOT_PROFILE_OWNER_MSG = "does not own the profile"; public static final String NOT_ORG_OWNED_PROFILE_OWNER_MSG = "not the profile owner on organization-owned device"; + public static final String INVALID_CALLING_IDENTITY_MSG = "Calling identity is not authorized"; public static final String ONGOING_CALL_MSG = "ongoing call on the device"; // TODO replace all instances of this with explicit {@link #mServiceContext}. @@ -2404,13 +2405,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set admin1 as DA. dpm.setActiveAdmin(admin1, false); assertTrue(dpm.isAdminActive(admin1)); - assertExpectException(SecurityException.class, /* messageRegex= */ NOT_DEVICE_OWNER_MSG, - () -> dpm.reboot(admin1)); + assertExpectException(SecurityException.class, /* messageRegex= */ + INVALID_CALLING_IDENTITY_MSG, () -> dpm.reboot(admin1)); // Set admin1 as PO. assertTrue(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM)); - assertExpectException(SecurityException.class, /* messageRegex= */ NOT_DEVICE_OWNER_MSG, - () -> dpm.reboot(admin1)); + assertExpectException(SecurityException.class, /* messageRegex= */ + INVALID_CALLING_IDENTITY_MSG, () -> dpm.reboot(admin1)); // Remove PO and add DO. dpm.clearProfileOwner(admin1); @@ -2623,7 +2624,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { UserHandle.myUserId(), UserManager.RESTRICTION_SOURCE_DEVICE_OWNER)) ).when(getServices().userManager).getUserRestrictionSources( eq(UserManager.DISALLOW_ADJUST_VOLUME), - eq(UserHandle.getUserHandleForUid(UserHandle.myUserId()))); + eq(UserHandle.of(UserHandle.myUserId()))); intent = dpm.createAdminSupportIntent(UserManager.DISALLOW_ADJUST_VOLUME); assertNotNull(intent); assertEquals(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS, intent.getAction()); diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java index e4c9cc3c05d9..1d914ec083fa 100644 --- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java @@ -88,36 +88,36 @@ public class InputMethodUtilsTest { public void testVoiceImes() throws Exception { // locale: en_US assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US, - "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); + "FakeDefaultEnKeyboardIme", "FakeDefaultAutoVoiceIme"); assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US, - "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0", - "DummyNonDefaultAutoVoiceIme1"); + "FakeDefaultEnKeyboardIme", "FakeNonDefaultAutoVoiceIme0", + "FakeNonDefaultAutoVoiceIme1"); assertDefaultEnabledMinimumImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US, - "DummyDefaultEnKeyboardIme"); + "FakeDefaultEnKeyboardIme"); assertDefaultEnabledMinimumImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US, - "DummyDefaultEnKeyboardIme"); + "FakeDefaultEnKeyboardIme"); // locale: en_GB assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB, - "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); + "FakeDefaultEnKeyboardIme", "FakeDefaultAutoVoiceIme"); assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB, - "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0", - "DummyNonDefaultAutoVoiceIme1"); + "FakeDefaultEnKeyboardIme", "FakeNonDefaultAutoVoiceIme0", + "FakeNonDefaultAutoVoiceIme1"); assertDefaultEnabledMinimumImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB, - "DummyDefaultEnKeyboardIme"); + "FakeDefaultEnKeyboardIme"); assertDefaultEnabledMinimumImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB, - "DummyDefaultEnKeyboardIme"); + "FakeDefaultEnKeyboardIme"); // locale: ja_JP assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP, - "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); + "FakeDefaultEnKeyboardIme", "FakeDefaultAutoVoiceIme"); assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, - "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0", - "DummyNonDefaultAutoVoiceIme1"); + "FakeDefaultEnKeyboardIme", "FakeNonDefaultAutoVoiceIme0", + "FakeNonDefaultAutoVoiceIme1"); assertDefaultEnabledMinimumImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP, - "DummyDefaultEnKeyboardIme"); + "FakeDefaultEnKeyboardIme"); assertDefaultEnabledMinimumImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, - "DummyDefaultEnKeyboardIme"); + "FakeDefaultEnKeyboardIme"); } @Test @@ -189,67 +189,67 @@ public class InputMethodUtilsTest { @Test public void testGetImplicitlyApplicableSubtypesLocked() throws Exception { - final InputMethodSubtype nonAutoEnUS = createDummyInputMethodSubtype("en_US", + final InputMethodSubtype nonAutoEnUS = createFakeInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoEnGB = createDummyInputMethodSubtype("en_GB", + final InputMethodSubtype nonAutoEnGB = createFakeInputMethodSubtype("en_GB", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoEnIN = createDummyInputMethodSubtype("en_IN", + final InputMethodSubtype nonAutoEnIN = createFakeInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoFrCA = createDummyInputMethodSubtype("fr_CA", + final InputMethodSubtype nonAutoFrCA = createFakeInputMethodSubtype("fr_CA", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoFr = createDummyInputMethodSubtype("fr_CA", + final InputMethodSubtype nonAutoFr = createFakeInputMethodSubtype("fr_CA", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoFil = createDummyInputMethodSubtype("fil", + final InputMethodSubtype nonAutoFil = createFakeInputMethodSubtype("fil", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoIn = createDummyInputMethodSubtype("in", + final InputMethodSubtype nonAutoIn = createFakeInputMethodSubtype("in", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoId = createDummyInputMethodSubtype("id", + final InputMethodSubtype nonAutoId = createFakeInputMethodSubtype("id", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype autoSubtype = createDummyInputMethodSubtype("auto", + final InputMethodSubtype autoSubtype = createFakeInputMethodSubtype("auto", SUBTYPE_MODE_KEYBOARD, !IS_AUX, IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoJa = createDummyInputMethodSubtype("ja", + final InputMethodSubtype nonAutoJa = createFakeInputMethodSubtype("ja", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoHi = createDummyInputMethodSubtype("hi", + final InputMethodSubtype nonAutoHi = createFakeInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoSrCyrl = createDummyInputMethodSubtype("sr", + final InputMethodSubtype nonAutoSrCyrl = createFakeInputMethodSubtype("sr", "sr-Cyrl", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoSrLatn = createDummyInputMethodSubtype("sr_ZZ", + final InputMethodSubtype nonAutoSrLatn = createFakeInputMethodSubtype("sr_ZZ", "sr-Latn", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoHandwritingEn = createDummyInputMethodSubtype("en", + final InputMethodSubtype nonAutoHandwritingEn = createFakeInputMethodSubtype("en", SUBTYPE_MODE_HANDWRITING, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoHandwritingFr = createDummyInputMethodSubtype("fr", + final InputMethodSubtype nonAutoHandwritingFr = createFakeInputMethodSubtype("fr", SUBTYPE_MODE_HANDWRITING, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoHandwritingSrCyrl = createDummyInputMethodSubtype("sr", + final InputMethodSubtype nonAutoHandwritingSrCyrl = createFakeInputMethodSubtype("sr", "sr-Cyrl", SUBTYPE_MODE_HANDWRITING, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoHandwritingSrLatn = createDummyInputMethodSubtype("sr_ZZ", + final InputMethodSubtype nonAutoHandwritingSrLatn = createFakeInputMethodSubtype("sr_ZZ", "sr-Latn", SUBTYPE_MODE_HANDWRITING, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); final InputMethodSubtype nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype = - createDummyInputMethodSubtype("zz", SUBTYPE_MODE_KEYBOARD, !IS_AUX, + createFakeInputMethodSubtype("zz", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); final InputMethodSubtype nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2 = - createDummyInputMethodSubtype("zz", SUBTYPE_MODE_KEYBOARD, !IS_AUX, + createFakeInputMethodSubtype("zz", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); @@ -266,9 +266,9 @@ public class InputMethodUtilsTest { subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); subtypes.add(nonAutoHandwritingEn); subtypes.add(nonAutoHandwritingFr); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -290,9 +290,9 @@ public class InputMethodUtilsTest { subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); subtypes.add(nonAutoHandwritingEn); subtypes.add(nonAutoHandwritingFr); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -314,9 +314,9 @@ public class InputMethodUtilsTest { subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); subtypes.add(nonAutoHandwritingEn); subtypes.add(nonAutoHandwritingFr); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -339,9 +339,9 @@ public class InputMethodUtilsTest { subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); subtypes.add(nonAutoHandwritingEn); subtypes.add(nonAutoHandwritingFr); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -360,9 +360,9 @@ public class InputMethodUtilsTest { subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); subtypes.add(nonAutoHandwritingEn); subtypes.add(nonAutoHandwritingFr); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -382,9 +382,9 @@ public class InputMethodUtilsTest { subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); subtypes.add(nonAutoHandwritingEn); subtypes.add(nonAutoHandwritingFr); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -404,9 +404,9 @@ public class InputMethodUtilsTest { subtypes.add(nonAutoHandwritingEn); subtypes.add(nonAutoHandwritingFr); subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -421,9 +421,9 @@ public class InputMethodUtilsTest { subtypes.add(nonAutoHandwritingEn); subtypes.add(nonAutoHandwritingFr); subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -438,9 +438,9 @@ public class InputMethodUtilsTest { subtypes.add(nonAutoEnUS); subtypes.add(nonAutoHi); subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -460,9 +460,9 @@ public class InputMethodUtilsTest { subtypes.add(nonAutoHandwritingFr); subtypes.add(nonAutoHandwritingSrCyrl); subtypes.add(nonAutoHandwritingSrLatn); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -480,9 +480,9 @@ public class InputMethodUtilsTest { subtypes.add(nonAutoHandwritingFr); subtypes.add(nonAutoHandwritingSrCyrl); subtypes.add(nonAutoHandwritingSrLatn); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -506,9 +506,9 @@ public class InputMethodUtilsTest { subtypes.add(nonAutoHandwritingFr); subtypes.add(nonAutoHandwritingSrCyrl); subtypes.add(nonAutoHandwritingSrLatn); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -533,9 +533,9 @@ public class InputMethodUtilsTest { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); subtypes.add(nonAutoEnUS); subtypes.add(nonAutoFil); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -551,9 +551,9 @@ public class InputMethodUtilsTest { subtypes.add(nonAutoJa); subtypes.add(nonAutoEnUS); subtypes.add(nonAutoFil); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -567,9 +567,9 @@ public class InputMethodUtilsTest { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); subtypes.add(nonAutoIn); subtypes.add(nonAutoEnUS); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -581,9 +581,9 @@ public class InputMethodUtilsTest { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); subtypes.add(nonAutoIn); subtypes.add(nonAutoEnUS); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -595,9 +595,9 @@ public class InputMethodUtilsTest { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); subtypes.add(nonAutoId); subtypes.add(nonAutoEnUS); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -609,9 +609,9 @@ public class InputMethodUtilsTest { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); subtypes.add(nonAutoId); subtypes.add(nonAutoEnUS); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -631,9 +631,9 @@ public class InputMethodUtilsTest { subtypes.add(nonAutoFil); subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); final ArrayList<InputMethodSubtype> result = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( @@ -649,22 +649,22 @@ public class InputMethodUtilsTest { @Test public void testContainsSubtypeOf() throws Exception { - final InputMethodSubtype nonAutoEnUS = createDummyInputMethodSubtype("en_US", + final InputMethodSubtype nonAutoEnUS = createFakeInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoEnGB = createDummyInputMethodSubtype("en_GB", + final InputMethodSubtype nonAutoEnGB = createFakeInputMethodSubtype("en_GB", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoFil = createDummyInputMethodSubtype("fil", + final InputMethodSubtype nonAutoFil = createFakeInputMethodSubtype("fil", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoFilPH = createDummyInputMethodSubtype("fil_PH", + final InputMethodSubtype nonAutoFilPH = createFakeInputMethodSubtype("fil_PH", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoIn = createDummyInputMethodSubtype("in", + final InputMethodSubtype nonAutoIn = createFakeInputMethodSubtype("in", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); - final InputMethodSubtype nonAutoId = createDummyInputMethodSubtype("id", + final InputMethodSubtype nonAutoId = createFakeInputMethodSubtype("id", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); @@ -673,9 +673,9 @@ public class InputMethodUtilsTest { { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); subtypes.add(nonAutoEnUS); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN, !CHECK_COUNTRY, @@ -705,9 +705,9 @@ public class InputMethodUtilsTest { { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); subtypes.add(nonAutoFil); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, !CHECK_COUNTRY, SUBTYPE_MODE_KEYBOARD)); @@ -732,9 +732,9 @@ public class InputMethodUtilsTest { { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); subtypes.add(nonAutoFilPH); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, !CHECK_COUNTRY, SUBTYPE_MODE_KEYBOARD)); @@ -760,9 +760,9 @@ public class InputMethodUtilsTest { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); subtypes.add(nonAutoIn); subtypes.add(nonAutoEnUS); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, !CHECK_COUNTRY, SUBTYPE_MODE_KEYBOARD)); @@ -779,9 +779,9 @@ public class InputMethodUtilsTest { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); subtypes.add(nonAutoId); subtypes.add(nonAutoEnUS); - final InputMethodInfo imi = createDummyInputMethodInfo( + final InputMethodInfo imi = createFakeInputMethodInfo( "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, IS_DEFAULT, subtypes); assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, !CHECK_COUNTRY, SUBTYPE_MODE_KEYBOARD)); @@ -866,7 +866,7 @@ public class InputMethodUtilsTest { assertEquals(expected.hashCode(), actual.hashCode()); } - private static InputMethodInfo createDummyInputMethodInfo(String packageName, String name, + private static InputMethodInfo createFakeInputMethodInfo(String packageName, String name, CharSequence label, boolean isAuxIme, boolean isDefault, List<InputMethodSubtype> subtypes) { final ResolveInfo ri = new ResolveInfo(); @@ -885,15 +885,15 @@ public class InputMethodUtilsTest { return new InputMethodInfo(ri, isAuxIme, "", subtypes, 1, isDefault); } - private static InputMethodSubtype createDummyInputMethodSubtype(String locale, String mode, + private static InputMethodSubtype createFakeInputMethodSubtype(String locale, String mode, boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, boolean isAsciiCapable, boolean isEnabledWhenDefaultIsNotAsciiCapable) { - return createDummyInputMethodSubtype(locale, null /* languageTag */, mode, isAuxiliary, + return createFakeInputMethodSubtype(locale, null /* languageTag */, mode, isAuxiliary, overridesImplicitlyEnabledSubtype, isAsciiCapable, isEnabledWhenDefaultIsNotAsciiCapable); } - private static InputMethodSubtype createDummyInputMethodSubtype(String locale, + private static InputMethodSubtype createFakeInputMethodSubtype(String locale, String languageTag, String mode, boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, boolean isAsciiCapable, boolean isEnabledWhenDefaultIsNotAsciiCapable) { @@ -920,14 +920,14 @@ public class InputMethodUtilsTest { ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>(); { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); - subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, + subtypes.add(createFakeInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, + subtypes.add(createFakeInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - preinstalledImes.add(createDummyInputMethodInfo("DummyDefaultAutoVoiceIme", - "dummy.voice0", "DummyVoice0", IS_AUX, IS_DEFAULT, subtypes)); + preinstalledImes.add(createFakeInputMethodInfo("FakeDefaultAutoVoiceIme", + "fake.voice0", "FakeVoice0", IS_AUX, IS_DEFAULT, subtypes)); } preinstalledImes.addAll(getImesWithoutDefaultVoiceIme()); return preinstalledImes; @@ -937,41 +937,41 @@ public class InputMethodUtilsTest { ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>(); { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); - subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, + subtypes.add(createFakeInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, + subtypes.add(createFakeInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme0", - "dummy.voice1", "DummyVoice1", IS_AUX, !IS_DEFAULT, subtypes)); + preinstalledImes.add(createFakeInputMethodInfo("FakeNonDefaultAutoVoiceIme0", + "fake.voice1", "FakeVoice1", IS_AUX, !IS_DEFAULT, subtypes)); } { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); - subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, + subtypes.add(createFakeInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, + subtypes.add(createFakeInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme1", - "dummy.voice2", "DummyVoice2", IS_AUX, !IS_DEFAULT, subtypes)); + preinstalledImes.add(createFakeInputMethodInfo("FakeNonDefaultAutoVoiceIme1", + "fake.voice2", "FakeVoice2", IS_AUX, !IS_DEFAULT, subtypes)); } { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); - subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, + subtypes.add(createFakeInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultVoiceIme2", - "dummy.voice3", "DummyVoice3", IS_AUX, !IS_DEFAULT, subtypes)); + preinstalledImes.add(createFakeInputMethodInfo("FakeNonDefaultVoiceIme2", + "fake.voice3", "FakeVoice3", IS_AUX, !IS_DEFAULT, subtypes)); } { final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); - subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX, + subtypes.add(createFakeInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - preinstalledImes.add(createDummyInputMethodInfo("DummyDefaultEnKeyboardIme", - "dummy.keyboard0", "DummyKeyboard0", !IS_AUX, IS_DEFAULT, subtypes)); + preinstalledImes.add(createFakeInputMethodInfo("FakeDefaultEnKeyboardIme", + "fake.keyboard0", "FakeKeyboard0", !IS_AUX, IS_DEFAULT, subtypes)); } return preinstalledImes; } @@ -991,91 +991,91 @@ public class InputMethodUtilsTest { private static ArrayList<InputMethodInfo> getSamplePreinstalledImes(final String localeString) { ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>(); - // a dummy Voice IME + // a fake Voice IME { final boolean isDefaultIme = false; final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); - subtypes.add(createDummyInputMethodSubtype("", SUBTYPE_MODE_VOICE, IS_AUX, + subtypes.add(createFakeInputMethodSubtype("", SUBTYPE_MODE_VOICE, IS_AUX, IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.voice", - "com.android.inputmethod.voice", "DummyVoiceIme", IS_AUX, isDefaultIme, + preinstalledImes.add(createFakeInputMethodInfo("com.android.apps.inputmethod.voice", + "com.android.inputmethod.voice", "FakeVoiceIme", IS_AUX, isDefaultIme, subtypes)); } - // a dummy Hindi IME + // a fake Hindi IME { final boolean isDefaultIme = contains(new String[]{ "hi", "en-rIN" }, localeString); final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); // TODO: This subtype should be marked as IS_ASCII_CAPABLE - subtypes.add(createDummyInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, + subtypes.add(createFakeInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX, + subtypes.add(createFakeInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.hindi", - "com.android.inputmethod.hindi", "DummyHindiIme", !IS_AUX, isDefaultIme, + preinstalledImes.add(createFakeInputMethodInfo("com.android.apps.inputmethod.hindi", + "com.android.inputmethod.hindi", "FakeHindiIme", !IS_AUX, isDefaultIme, subtypes)); } - // a dummy Pinyin IME + // a fake Pinyin IME { final boolean isDefaultIme = contains(new String[]{ "zh-rCN" }, localeString); final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); - subtypes.add(createDummyInputMethodSubtype("zh_CN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, + subtypes.add(createFakeInputMethodSubtype("zh_CN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.pinyin", - "com.android.apps.inputmethod.pinyin", "DummyPinyinIme", !IS_AUX, isDefaultIme, + preinstalledImes.add(createFakeInputMethodInfo("com.android.apps.inputmethod.pinyin", + "com.android.apps.inputmethod.pinyin", "FakePinyinIme", !IS_AUX, isDefaultIme, subtypes)); } - // a dummy Korean IME + // a fake Korean IME { final boolean isDefaultIme = contains(new String[]{ "ko" }, localeString); final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); - subtypes.add(createDummyInputMethodSubtype("ko", SUBTYPE_MODE_KEYBOARD, !IS_AUX, + subtypes.add(createFakeInputMethodSubtype("ko", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.korean", - "com.android.apps.inputmethod.korean", "DummyKoreanIme", !IS_AUX, isDefaultIme, + preinstalledImes.add(createFakeInputMethodInfo("com.android.apps.inputmethod.korean", + "com.android.apps.inputmethod.korean", "FakeKoreanIme", !IS_AUX, isDefaultIme, subtypes)); } - // a dummy Latin IME + // a fake Latin IME { final boolean isDefaultIme = contains( new String[]{ "en-rUS", "en-rGB", "en-rIN", "en", "hi" }, localeString); final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); - subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX, + subtypes.add(createFakeInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - subtypes.add(createDummyInputMethodSubtype("en_GB", SUBTYPE_MODE_KEYBOARD, !IS_AUX, + subtypes.add(createFakeInputMethodSubtype("en_GB", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - subtypes.add(createDummyInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, + subtypes.add(createFakeInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX, + subtypes.add(createFakeInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, isDefaultIme, + preinstalledImes.add(createFakeInputMethodInfo("com.android.apps.inputmethod.latin", + "com.android.apps.inputmethod.latin", "FakeLatinIme", !IS_AUX, isDefaultIme, subtypes)); } - // a dummy Japanese IME + // a fake Japanese IME { final boolean isDefaultIme = contains(new String[]{ "ja", "ja-rJP" }, localeString); final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); - subtypes.add(createDummyInputMethodSubtype("ja", SUBTYPE_MODE_KEYBOARD, !IS_AUX, + subtypes.add(createFakeInputMethodSubtype("ja", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - subtypes.add(createDummyInputMethodSubtype("emoji", SUBTYPE_MODE_KEYBOARD, !IS_AUX, + subtypes.add(createFakeInputMethodSubtype("emoji", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); - preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.japanese", - "com.android.apps.inputmethod.japanese", "DummyJapaneseIme", !IS_AUX, + preinstalledImes.add(createFakeInputMethodInfo("com.android.apps.inputmethod.japanese", + "com.android.apps.inputmethod.japanese", "FakeJapaneseIme", !IS_AUX, isDefaultIme, subtypes)); } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java index 1b5c56a4b4c9..d44d37e4e2a1 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java @@ -19,7 +19,6 @@ package com.android.server.locksettings; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; @@ -169,7 +168,7 @@ public abstract class BaseLockSettingsServiceTests { final ArrayList<UserInfo> allUsers = new ArrayList<>(mPrimaryUserProfiles); allUsers.add(SECONDARY_USER_INFO); - when(mUserManager.getUsers(anyBoolean())).thenReturn(allUsers); + when(mUserManager.getUsers()).thenReturn(allUsers); when(mActivityManager.unlockUser(anyInt(), any(), any(), any())).thenAnswer( new Answer<Boolean>() { diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt index 9f9ec31f0c91..f96ebda67602 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt @@ -23,6 +23,8 @@ import com.google.common.truth.Expect import org.junit.Rule import org.junit.Test +import org.junit.rules.Timeout +import java.util.concurrent.TimeUnit /** * Collects APKs from the device and verifies that the new parsing behavior outputs @@ -32,6 +34,9 @@ import org.junit.Test class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() { @get:Rule + val timeout = Timeout(4, TimeUnit.MINUTES) + + @get:Rule val expect = Expect.create() @Test diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java index 083df28b2278..aaa74dd832af 100644 --- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java @@ -284,28 +284,29 @@ public class ThermalManagerServiceTest { @Test public void testNotify() throws RemoteException { int status = Temperature.THROTTLING_SEVERE; + // Should only notify event not status Temperature newBattery = new Temperature(50, Temperature.TYPE_BATTERY, "batt", status); mFakeHal.mCallback.onValues(newBattery); verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) .times(1)).notifyThrottling(newBattery); verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) - .times(1)).onStatusChange(status); + .times(0)).onStatusChange(anyInt()); verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) .times(0)).notifyThrottling(newBattery); verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) - .times(1)).onStatusChange(status); + .times(0)).onStatusChange(anyInt()); resetListenerMock(); - // Should only notify event not status + // Notify both event and status Temperature newSkin = new Temperature(50, Temperature.TYPE_SKIN, "skin1", status); mFakeHal.mCallback.onValues(newSkin); verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) .times(1)).notifyThrottling(newSkin); verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) - .times(0)).onStatusChange(anyInt()); + .times(1)).onStatusChange(status); verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) .times(1)).notifyThrottling(newSkin); verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) - .times(0)).onStatusChange(anyInt()); + .times(1)).onStatusChange(status); resetListenerMock(); // Back to None, should only notify event not status status = Temperature.THROTTLING_NONE; @@ -345,10 +346,13 @@ public class ThermalManagerServiceTest { @Test public void testGetCurrentStatus() throws RemoteException { - int status = Temperature.THROTTLING_EMERGENCY; + int status = Temperature.THROTTLING_SEVERE; Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status); mFakeHal.mCallback.onValues(newSkin); assertEquals(status, mService.mService.getCurrentThermalStatus()); + int battStatus = Temperature.THROTTLING_EMERGENCY; + Temperature newBattery = new Temperature(60, Temperature.TYPE_BATTERY, "batt", battStatus); + assertEquals(status, mService.mService.getCurrentThermalStatus()); } @Test diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java index dcf319058ca2..e5e931115c05 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java @@ -27,6 +27,9 @@ import android.app.timezonedetector.TimeZoneCapabilities; import android.app.timezonedetector.TimeZoneConfiguration; import android.util.IndentingPrintWriter; +import java.util.ArrayList; +import java.util.List; + class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { private StrategyListener mListener; @@ -41,6 +44,7 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { private TelephonyTimeZoneSuggestion mLastTelephonySuggestion; private boolean mHandleAutoTimeZoneConfigChangedCalled; private boolean mDumpCalled; + private final List<Dumpable> mDumpables = new ArrayList<>(); @Override public void setStrategyListener(@NonNull StrategyListener listener) { @@ -105,7 +109,7 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { @Override public void addDumpable(Dumpable dumpable) { - // Stubbed + mDumpables.add(dumpable); } @Override @@ -149,4 +153,8 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { void verifyDumpCalled() { assertTrue(mDumpCalled); } + + void verifyHasDumpable(Dumpable expected) { + assertTrue(mDumpables.contains(expected)); + } } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java index 0e2c22756097..e9d57e52ce69 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java @@ -75,6 +75,16 @@ public class TimeZoneDetectorInternalImplTest { mFakeTimeZoneDetectorStrategy.verifySuggestGeolocationTimeZoneCalled(timeZoneSuggestion); } + @Test + public void testAddDumpable() throws Exception { + Dumpable stubbedDumpable = mock(Dumpable.class); + + mTimeZoneDetectorInternal.addDumpable(stubbedDumpable); + mTestHandler.assertTotalMessagesEnqueued(0); + + mFakeTimeZoneDetectorStrategy.verifyHasDumpable(stubbedDumpable); + } + private static GeolocationTimeZoneSuggestion createGeolocationTimeZoneSuggestion() { return new GeolocationTimeZoneSuggestion(ARBITRARY_ZONE_IDS); } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java index 8034cacc6923..3a1ec4f90d7a 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java @@ -52,6 +52,7 @@ import org.junit.runner.RunWith; import java.io.PrintWriter; import java.io.StringWriter; +import java.util.Arrays; @RunWith(AndroidJUnit4.class) public class TimeZoneDetectorServiceTest { @@ -253,6 +254,39 @@ public class TimeZoneDetectorServiceTest { } @Test(expected = SecurityException.class) + public void testSuggestGeolocationTimeZone_withoutPermission() { + doThrow(new SecurityException("Mock")) + .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); + GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion(); + + try { + mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion); + fail(); + } finally { + verify(mMockContext).enforceCallingOrSelfPermission( + eq(android.Manifest.permission.SET_TIME_ZONE), + anyString()); + } + } + + @Test + public void testSuggestGeolocationTimeZone() throws Exception { + doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); + + GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion(); + + mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion); + mTestHandler.assertTotalMessagesEnqueued(1); + + verify(mMockContext).enforceCallingOrSelfPermission( + eq(android.Manifest.permission.SET_TIME_ZONE), + anyString()); + + mTestHandler.waitForMessagesToBeProcessed(); + mFakeTimeZoneDetectorStrategy.verifySuggestGeolocationTimeZoneCalled(timeZoneSuggestion); + } + + @Test(expected = SecurityException.class) public void testSuggestManualTimeZone_withoutPermission() { doThrow(new SecurityException("Mock")) .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); @@ -346,7 +380,7 @@ public class TimeZoneDetectorServiceTest { } @Test - public void testAutoTimeZoneDetectionChanged() throws Exception { + public void testHandleAutoTimeZoneConfigChanged() throws Exception { mTimeZoneDetectorService.handleAutoTimeZoneConfigChanged(); mTestHandler.assertTotalMessagesEnqueued(1); mTestHandler.waitForMessagesToBeProcessed(); @@ -370,10 +404,15 @@ public class TimeZoneDetectorServiceTest { private static TimeZoneCapabilities createTimeZoneCapabilities() { return new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID) .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED) + .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED) .setSuggestManualTimeZone(CAPABILITY_POSSESSED) .build(); } + private static GeolocationTimeZoneSuggestion createGeolocationTimeZoneSuggestion() { + return new GeolocationTimeZoneSuggestion(Arrays.asList("TestZoneId")); + } + private static ManualTimeZoneSuggestion createManualTimeZoneSuggestion() { return new ManualTimeZoneSuggestion("TestZoneId"); } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java index 68554451e43a..a6caa4299ef6 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java @@ -41,6 +41,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; @@ -56,10 +57,10 @@ import org.junit.Before; import org.junit.Test; import java.io.StringWriter; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; +import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; @@ -93,16 +94,26 @@ public class TimeZoneDetectorStrategyImplTest { TELEPHONY_SCORE_HIGHEST), }; - private static final TimeZoneConfiguration CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED = + private static final TimeZoneConfiguration CONFIG_AUTO_ENABLED = new TimeZoneConfiguration.Builder() .setAutoDetectionEnabled(true) .build(); - private static final TimeZoneConfiguration CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED = + private static final TimeZoneConfiguration CONFIG_AUTO_DISABLED = new TimeZoneConfiguration.Builder() .setAutoDetectionEnabled(false) .build(); + private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_DISABLED = + new TimeZoneConfiguration.Builder() + .setGeoDetectionEnabled(false) + .build(); + + private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_ENABLED = + new TimeZoneConfiguration.Builder() + .setGeoDetectionEnabled(true) + .build(); + private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy; private FakeCallback mFakeCallback; private MockStrategyListener mMockStrategyListener; @@ -120,7 +131,7 @@ public class TimeZoneDetectorStrategyImplTest { public void testGetCapabilities() { new Script() .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); TimeZoneCapabilities expectedCapabilities = mFakeCallback.getCapabilities(USER_ID); assertEquals(expectedCapabilities, mTimeZoneDetectorStrategy.getCapabilities(USER_ID)); } @@ -129,7 +140,7 @@ public class TimeZoneDetectorStrategyImplTest { public void testGetConfiguration() { new Script() .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); TimeZoneConfiguration expectedConfiguration = mFakeCallback.getConfiguration(USER_ID); assertTrue(expectedConfiguration.isComplete()); assertEquals(expectedConfiguration, mTimeZoneDetectorStrategy.getConfiguration(USER_ID)); @@ -140,20 +151,22 @@ public class TimeZoneDetectorStrategyImplTest { Script script = new Script(); script.initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); { // Check the fake test infra is doing what is expected. TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID); assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled()); + assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled()); assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getSuggestManualTimeZone()); } script.initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED); + CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)); { // Check the fake test infra is doing what is expected. TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID); assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled()); + assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled()); assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone()); } } @@ -163,20 +176,22 @@ public class TimeZoneDetectorStrategyImplTest { Script script = new Script(); script.initializeUser(USER_ID, UserCase.RESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); { // Check the fake test infra is doing what is expected. TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID); assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled()); + assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled()); assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone()); } script.initializeUser(USER_ID, UserCase.RESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED); + CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)); { // Check the fake test infra is doing what is expected. TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID); assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled()); + assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled()); assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone()); } } @@ -186,20 +201,22 @@ public class TimeZoneDetectorStrategyImplTest { Script script = new Script(); script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); { // Check the fake test infra is doing what is expected. TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID); assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled()); + assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled()); assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone()); } script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED); + CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)); { // Check the fake test infra is doing what is expected. TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID); assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled()); + assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled()); assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone()); } } @@ -208,42 +225,61 @@ public class TimeZoneDetectorStrategyImplTest { public void testUpdateConfiguration_unrestricted() { Script script = new Script() .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); // Set the configuration with auto detection enabled. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */); // Nothing should have happened: it was initialized in this state. script.verifyConfigurationNotChanged(); // Update the configuration with auto detection disabled. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED); + script.simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */); // The settings should have been changed and the StrategyListener onChange() called. - script.verifyConfigurationChangedAndReset( - USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED); + script.verifyConfigurationChangedAndReset(USER_ID, + CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)); // Update the configuration with auto detection enabled. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */); // The settings should have been changed and the StrategyListener onChange() called. - script.verifyConfigurationChangedAndReset(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + script.verifyConfigurationChangedAndReset(USER_ID, + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); + + // Update the configuration to enable geolocation time zone detection. + script.simulateUpdateConfiguration( + USER_ID, CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */); + + // The settings should have been changed and the StrategyListener onChange() called. + script.verifyConfigurationChangedAndReset(USER_ID, + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED)); } @Test public void testUpdateConfiguration_restricted() { Script script = new Script() .initializeUser(USER_ID, UserCase.RESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); // Try to update the configuration with auto detection disabled. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED); + script.simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_DISABLED, false /* expectedResult */); // The settings should not have been changed: user shouldn't have the capabilities. script.verifyConfigurationNotChanged(); // Update the configuration with auto detection enabled. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + script.simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_ENABLED, false /* expectedResult */); + + // The settings should not have been changed: user shouldn't have the capabilities. + script.verifyConfigurationNotChanged(); + + // Update the configuration to enable geolocation time zone detection. + script.simulateUpdateConfiguration( + USER_ID, CONFIG_GEO_DETECTION_ENABLED, false /* expectedResult */); // The settings should not have been changed: user shouldn't have the capabilities. script.verifyConfigurationNotChanged(); @@ -253,16 +289,18 @@ public class TimeZoneDetectorStrategyImplTest { public void testUpdateConfiguration_autoDetectNotSupported() { Script script = new Script() .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); // Try to update the configuration with auto detection disabled. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED); + script.simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_DISABLED, false /* expectedResult */); // The settings should not have been changed: user shouldn't have the capabilities. script.verifyConfigurationNotChanged(); // Update the configuration with auto detection enabled. - script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + script.simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_ENABLED, false /* expectedResult */); // The settings should not have been changed: user shouldn't have the capabilities. script.verifyConfigurationNotChanged(); @@ -276,7 +314,7 @@ public class TimeZoneDetectorStrategyImplTest { createEmptySlotIndex2Suggestion(); Script script = new Script() .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED) + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); script.simulateTelephonyTimeZoneSuggestion(slotIndex1TimeZoneSuggestion) @@ -308,6 +346,10 @@ public class TimeZoneDetectorStrategyImplTest { mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); } + /** + * Telephony suggestions have quality metadata. Ordinarily, low scoring suggestions are not + * used, but this is not true if the device's time zone setting is uninitialized. + */ @Test public void testTelephonySuggestionsWhenTimeZoneUninitialized() { assertTrue(TELEPHONY_SCORE_LOW < TELEPHONY_SCORE_USAGE_THRESHOLD); @@ -319,7 +361,7 @@ public class TimeZoneDetectorStrategyImplTest { Script script = new Script() .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); // A low quality suggestions will not be taken: The device time zone setting is left // uninitialized. @@ -376,16 +418,16 @@ public class TimeZoneDetectorStrategyImplTest { /** * Confirms that toggling the auto time zone detection setting has the expected behavior when - * the strategy is "opinionated". + * the strategy is "opinionated" when using telephony auto detection. */ @Test - public void testTogglingAutoTimeZoneDetection() { + public void testTogglingAutoDetection_autoTelephony() { Script script = new Script(); for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) { // Start with the device in a known state. script.initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED) + CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); TelephonyTimeZoneSuggestion suggestion = @@ -405,7 +447,8 @@ public class TimeZoneDetectorStrategyImplTest { mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); // Toggling the time zone setting on should cause the device setting to be set. - script.simulateAutoTimeZoneDetectionEnabled(USER_ID, true); + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, + true /* expectedResult */); // When time zone detection is already enabled the suggestion (if it scores highly // enough) should be set immediately. @@ -422,7 +465,8 @@ public class TimeZoneDetectorStrategyImplTest { mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); // Toggling the time zone setting should off should do nothing. - script.simulateAutoTimeZoneDetectionEnabled(USER_ID, false) + script.simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) .verifyTimeZoneNotChanged(); // Assert internal service state. @@ -437,7 +481,7 @@ public class TimeZoneDetectorStrategyImplTest { public void testTelephonySuggestionsSingleSlotId() { Script script = new Script() .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED) + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) { @@ -451,8 +495,7 @@ public class TimeZoneDetectorStrategyImplTest { */ // Each test case will have the same or lower score than the last. - ArrayList<TelephonyTestCase> descendingCasesByScore = - new ArrayList<>(Arrays.asList(TELEPHONY_TEST_CASES)); + List<TelephonyTestCase> descendingCasesByScore = list(TELEPHONY_TEST_CASES); Collections.reverse(descendingCasesByScore); for (TelephonyTestCase testCase : descendingCasesByScore) { @@ -504,7 +547,7 @@ public class TimeZoneDetectorStrategyImplTest { Script script = new Script() .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED) + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID) // Initialize the latest suggestions as empty so we don't need to worry about nulls // below for the first loop. @@ -583,15 +626,15 @@ public class TimeZoneDetectorStrategyImplTest { } /** - * The {@link TimeZoneDetectorStrategyImpl.Callback} is left to detect whether changing - * the time zone is actually necessary. This test proves that the service doesn't assume it - * knows the current setting. + * The {@link TimeZoneDetectorStrategyImpl.Callback} is left to detect whether changing the time + * zone is actually necessary. This test proves that the strategy doesn't assume it knows the + * current settings. */ @Test - public void testTelephonySuggestionTimeZoneDetectorStrategyDoesNotAssumeCurrentSetting() { + public void testTelephonySuggestionStrategyDoesNotAssumeCurrentSetting_autoTelephony() { Script script = new Script() .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED); + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)); TelephonyTestCase testCase = newTelephonyTestCase( MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH); @@ -609,26 +652,40 @@ public class TimeZoneDetectorStrategyImplTest { // Toggling time zone detection should set the device time zone only if the current setting // value is different from the most recent telephony suggestion. - script.simulateAutoTimeZoneDetectionEnabled(USER_ID, false) + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) .verifyTimeZoneNotChanged() - .simulateAutoTimeZoneDetectionEnabled(USER_ID, true) + .simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) .verifyTimeZoneNotChanged(); // Simulate a user turning auto detection off, a new suggestion being made while auto // detection is off, and the user turning it on again. - script.simulateAutoTimeZoneDetectionEnabled(USER_ID, false) + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) .simulateTelephonyTimeZoneSuggestion(newYorkSuggestion) .verifyTimeZoneNotChanged(); // Latest suggestion should be used. - script.simulateAutoTimeZoneDetectionEnabled(USER_ID, true) + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) .verifyTimeZoneChangedAndReset(newYorkSuggestion); } @Test - public void testManualSuggestion_unrestricted_simulateAutoTimeZoneEnabled() { + public void testManualSuggestion_autoDetectionEnabled_autoTelephony() { + checkManualSuggestion_autoDetectionEnabled(false /* geoDetectionEnabled */); + } + + @Test + public void testManualSuggestion_autoDetectionEnabled_autoGeo() { + checkManualSuggestion_autoDetectionEnabled(true /* geoDetectionEnabled */); + } + + private void checkManualSuggestion_autoDetectionEnabled(boolean geoDetectionEnabled) { + TimeZoneConfiguration geoTzEnabledConfig = + new TimeZoneConfiguration.Builder() + .setGeoDetectionEnabled(geoDetectionEnabled) + .build(); Script script = new Script() .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED) + CONFIG_AUTO_ENABLED.with(geoTzEnabledConfig)) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); // Auto time zone detection is enabled so the manual suggestion should be ignored. @@ -641,7 +698,7 @@ public class TimeZoneDetectorStrategyImplTest { public void testManualSuggestion_restricted_simulateAutoTimeZoneEnabled() { Script script = new Script() .initializeUser(USER_ID, UserCase.RESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED) + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); // Auto time zone detection is enabled so the manual suggestion should be ignored. @@ -654,7 +711,7 @@ public class TimeZoneDetectorStrategyImplTest { public void testManualSuggestion_autoDetectNotSupported_simulateAutoTimeZoneEnabled() { Script script = new Script() .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED) + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED)) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); // Auto time zone detection is enabled so the manual suggestion should be ignored. @@ -668,7 +725,7 @@ public class TimeZoneDetectorStrategyImplTest { public void testManualSuggestion_unrestricted_autoTimeZoneDetectionDisabled() { Script script = new Script() .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED) + CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); // Auto time zone detection is disabled so the manual suggestion should be used. @@ -682,7 +739,7 @@ public class TimeZoneDetectorStrategyImplTest { public void testManualSuggestion_restricted_autoTimeZoneDetectionDisabled() { Script script = new Script() .initializeUser(USER_ID, UserCase.RESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED) + CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); // Restricted users do not have the capability. @@ -696,7 +753,7 @@ public class TimeZoneDetectorStrategyImplTest { public void testManualSuggestion_autoDetectNotSupported_autoTimeZoneDetectionDisabled() { Script script = new Script() .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED) + CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); // Unrestricted users have the capability. @@ -707,10 +764,261 @@ public class TimeZoneDetectorStrategyImplTest { } @Test + public void testGeoSuggestion_uncertain() { + Script script = new Script() + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED)) + .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); + + GeolocationTimeZoneSuggestion uncertainSuggestion = createUncertainGeoLocationSuggestion(); + + script.simulateGeolocationTimeZoneSuggestion(uncertainSuggestion) + .verifyTimeZoneNotChanged(); + + // Assert internal service state. + assertEquals(uncertainSuggestion, + mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); + } + + @Test + public void testGeoSuggestion_noZones() { + Script script = new Script() + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED)) + .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); + + GeolocationTimeZoneSuggestion noZonesSuggestion = createGeoLocationSuggestion(list()); + + script.simulateGeolocationTimeZoneSuggestion(noZonesSuggestion) + .verifyTimeZoneNotChanged(); + + // Assert internal service state. + assertEquals(noZonesSuggestion, mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); + } + + @Test + public void testGeoSuggestion_oneZone() { + GeolocationTimeZoneSuggestion suggestion = + createGeoLocationSuggestion(list("Europe/London")); + + Script script = new Script() + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED)) + .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); + + script.simulateGeolocationTimeZoneSuggestion(suggestion) + .verifyTimeZoneChangedAndReset("Europe/London"); + + // Assert internal service state. + assertEquals(suggestion, mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); + } + + /** + * In the current implementation, the first zone ID is always used unless the device is set to + * one of the other options. This is "stickiness" - the device favors the zone it is currently + * set to until that unambiguously can't be correct. + */ + @Test + public void testGeoSuggestion_multiZone() { + GeolocationTimeZoneSuggestion londonOnlySuggestion = + createGeoLocationSuggestion(list("Europe/London")); + GeolocationTimeZoneSuggestion londonOrParisSuggestion = + createGeoLocationSuggestion(list("Europe/Paris", "Europe/London")); + GeolocationTimeZoneSuggestion parisOnlySuggestion = + createGeoLocationSuggestion(list("Europe/Paris")); + + Script script = new Script() + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED)) + .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); + + script.simulateGeolocationTimeZoneSuggestion(londonOnlySuggestion) + .verifyTimeZoneChangedAndReset("Europe/London"); + assertEquals(londonOnlySuggestion, + mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); + + // Confirm bias towards the current device zone when there's multiple zones to choose from. + script.simulateGeolocationTimeZoneSuggestion(londonOrParisSuggestion) + .verifyTimeZoneNotChanged(); + assertEquals(londonOrParisSuggestion, + mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); + + script.simulateGeolocationTimeZoneSuggestion(parisOnlySuggestion) + .verifyTimeZoneChangedAndReset("Europe/Paris"); + assertEquals(parisOnlySuggestion, + mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); + + // Now the suggestion that previously left the device on Europe/London will leave the device + // on Europe/Paris. + script.simulateGeolocationTimeZoneSuggestion(londonOrParisSuggestion) + .verifyTimeZoneNotChanged(); + assertEquals(londonOrParisSuggestion, + mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); + } + + /** + * Confirms that toggling the auto time zone detection enabled setting has the expected behavior + * when the strategy is "opinionated" and "un-opinionated" when in geolocation detection is + * enabled. + */ + @Test + public void testTogglingAutoDetectionEnabled_autoGeo() { + GeolocationTimeZoneSuggestion geolocationSuggestion = + createGeoLocationSuggestion(list("Europe/London")); + GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion = + createUncertainGeoLocationSuggestion(); + ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris"); + + Script script = new Script() + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_ENABLED)) + .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); + + script.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion); + + // When time zone detection is not enabled, the time zone suggestion will not be set. + script.verifyTimeZoneNotChanged(); + + // Assert internal service state. + assertEquals(geolocationSuggestion, + mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); + + // Toggling the time zone setting on should cause the device setting to be set. + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) + .verifyTimeZoneChangedAndReset("Europe/London"); + + // Toggling the time zone setting should off should do nothing because the device is now + // set to that time zone. + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) + .verifyTimeZoneNotChanged() + .simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) + .verifyTimeZoneNotChanged(); + + // Now toggle auto time zone setting, and confirm it is opinionated. + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) + .simulateManualTimeZoneSuggestion( + USER_ID, manualSuggestion, true /* expectedResult */) + .verifyTimeZoneChangedAndReset(manualSuggestion) + .simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) + .verifyTimeZoneChangedAndReset("Europe/London"); + + // Now withdraw the geolocation suggestion, and assert the strategy is no longer + // opinionated. + /* expectedResult */ + script.simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion) + .verifyTimeZoneNotChanged() + .simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) + .verifyTimeZoneNotChanged() + .simulateManualTimeZoneSuggestion( + USER_ID, manualSuggestion, true /* expectedResult */) + .verifyTimeZoneChangedAndReset(manualSuggestion) + .simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) + .verifyTimeZoneNotChanged(); + + // Assert internal service state. + assertEquals(uncertainGeolocationSuggestion, + mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); + } + + /** + * Confirms that changing the geolocation time zone detection enabled setting has the expected + * behavior, i.e. immediately recompute the detected time zone using different signals. + */ + @Test + public void testChangingGeoDetectionEnabled() { + GeolocationTimeZoneSuggestion geolocationSuggestion = + createGeoLocationSuggestion(list("Europe/London")); + TelephonyTimeZoneSuggestion telephonySuggestion = createTelephonySuggestion( + SLOT_INDEX1, MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, + "Europe/Paris"); + + Script script = new Script() + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)) + .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); + + // Add suggestions. Nothing should happen as time zone detection is disabled. + script.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion) + .verifyTimeZoneNotChanged(); + script.simulateTelephonyTimeZoneSuggestion(telephonySuggestion) + .verifyTimeZoneNotChanged(); + + // Assert internal service state. + assertEquals(geolocationSuggestion, + mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); + assertEquals(telephonySuggestion, + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1).suggestion); + + // Toggling the time zone detection enabled setting on should cause the device setting to be + // set from the telephony signal, as we've started with geolocation time zone detection + // disabled. + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) + .verifyTimeZoneChangedAndReset(telephonySuggestion); + + // Changing the detection to enable geo detection should cause the device tz setting to + // change to the geo suggestion. + script.simulateUpdateConfiguration( + USER_ID, CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */) + .verifyTimeZoneChangedAndReset(geolocationSuggestion.getZoneIds().get(0)); + + // Changing the detection to disable geo detection should cause the device tz setting to + // change to the telephony suggestion. + script.simulateUpdateConfiguration( + USER_ID, CONFIG_GEO_DETECTION_DISABLED, true /* expectedResult */) + .verifyTimeZoneChangedAndReset(telephonySuggestion); + } + + /** + * The {@link TimeZoneDetectorStrategyImpl.Callback} is left to detect whether changing the time + * zone is actually necessary. This test proves that the strategy doesn't assume it knows the + * current setting. + */ + @Test + public void testTimeZoneDetectorStrategyDoesNotAssumeCurrentSetting_autoGeo() { + GeolocationTimeZoneSuggestion losAngelesSuggestion = + createGeoLocationSuggestion(list("America/Los_Angeles")); + GeolocationTimeZoneSuggestion newYorkSuggestion = + createGeoLocationSuggestion(list("America/New_York")); + + Script script = new Script() + .initializeUser(USER_ID, UserCase.UNRESTRICTED, + CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED)); + + // Initialization. + script.simulateGeolocationTimeZoneSuggestion(losAngelesSuggestion) + .verifyTimeZoneChangedAndReset("America/Los_Angeles"); + // Suggest it again - it should not be set because it is already set. + script.simulateGeolocationTimeZoneSuggestion(losAngelesSuggestion) + .verifyTimeZoneNotChanged(); + + // Toggling time zone detection should set the device time zone only if the current setting + // value is different from the most recent telephony suggestion. + /* expectedResult */ + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) + .verifyTimeZoneNotChanged() + .simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) + .verifyTimeZoneNotChanged(); + + // Simulate a user turning auto detection off, a new suggestion being made while auto + // detection is off, and the user turning it on again. + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) + .simulateGeolocationTimeZoneSuggestion(newYorkSuggestion) + .verifyTimeZoneNotChanged(); + // Latest suggestion should be used. + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) + .verifyTimeZoneChangedAndReset("America/New_York"); + } + + @Test public void testAddDumpable() { new Script() .initializeUser(USER_ID, UserCase.UNRESTRICTED, - CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED) + CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED)) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); AtomicBoolean dumpCalled = new AtomicBoolean(false); @@ -733,6 +1041,15 @@ public class TimeZoneDetectorStrategyImplTest { return new ManualTimeZoneSuggestion(zoneId); } + private static TelephonyTimeZoneSuggestion createTelephonySuggestion( + int slotIndex, @MatchType int matchType, @Quality int quality, String zoneId) { + return new TelephonyTimeZoneSuggestion.Builder(slotIndex) + .setMatchType(matchType) + .setQuality(quality) + .setZoneId(zoneId) + .build(); + } + private static TelephonyTimeZoneSuggestion createEmptySlotIndex1Suggestion() { return new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX1).build(); } @@ -741,6 +1058,17 @@ public class TimeZoneDetectorStrategyImplTest { return new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX2).build(); } + private static GeolocationTimeZoneSuggestion createUncertainGeoLocationSuggestion() { + return createGeoLocationSuggestion(null); + } + + private static GeolocationTimeZoneSuggestion createGeoLocationSuggestion( + @Nullable List<String> zoneIds) { + GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(zoneIds); + suggestion.addDebugInfo("Test suggestion"); + return suggestion; + } + static class FakeCallback implements TimeZoneDetectorStrategyImpl.Callback { private TimeZoneCapabilities mCapabilities; @@ -757,7 +1085,8 @@ public class TimeZoneDetectorStrategyImplTest { TimeZoneConfiguration configuration) { assertEquals(userId, capabilities.getUserId()); mCapabilities = capabilities; - assertTrue(configuration.isComplete()); + assertTrue("Configuration must be complete when initializing, config=" + configuration, + configuration.isComplete()); mConfiguration.init(new UserConfiguration(userId, configuration)); } @@ -790,11 +1119,8 @@ public class TimeZoneDetectorStrategyImplTest { mConfiguration.set(new UserConfiguration(userId, newConfig)); if (!newConfig.equals(oldConfig)) { - if (oldConfig.isAutoDetectionEnabled() != newConfig.isAutoDetectionEnabled()) { - // Simulate what happens when the auto detection enabled configuration is - // changed. - mStrategy.handleAutoTimeZoneConfigChanged(); - } + // Simulate what happens when the auto detection configuration is changed. + mStrategy.handleAutoTimeZoneConfigChanged(); } } @@ -804,6 +1130,11 @@ public class TimeZoneDetectorStrategyImplTest { } @Override + public boolean isGeoDetectionEnabled() { + return mConfiguration.getLatest().configuration.isGeoDetectionEnabled(); + } + + @Override public boolean isDeviceTimeZoneInitialized() { return mTimeZoneId.getLatest() != null; } @@ -942,32 +1273,33 @@ public class TimeZoneDetectorStrategyImplTest { * supplied configuration. */ private static TimeZoneCapabilities createCapabilities( - int userId, UserCase userRole, TimeZoneConfiguration configuration) { - switch (userRole) { + int userId, UserCase userCase, TimeZoneConfiguration configuration) { + switch (userCase) { case UNRESTRICTED: { int suggestManualTimeZoneCapability = configuration.isAutoDetectionEnabled() ? CAPABILITY_NOT_APPLICABLE : CAPABILITY_POSSESSED; return new TimeZoneCapabilities.Builder(userId) .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED) + .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED) .setSuggestManualTimeZone(suggestManualTimeZoneCapability) .build(); } case RESTRICTED: { return new TimeZoneCapabilities.Builder(userId) .setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED) + .setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED) .setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED) .build(); - } case AUTO_DETECT_NOT_SUPPORTED: { return new TimeZoneCapabilities.Builder(userId) .setConfigureAutoDetectionEnabled(CAPABILITY_NOT_SUPPORTED) + .setConfigureGeoDetectionEnabled(CAPABILITY_NOT_SUPPORTED) .setSuggestManualTimeZone(CAPABILITY_POSSESSED) .build(); - } default: - throw new AssertionError(userRole + " not recognized"); + throw new AssertionError(userCase + " not recognized"); } } @@ -978,8 +1310,8 @@ public class TimeZoneDetectorStrategyImplTest { private class Script { Script initializeUser( - @UserIdInt int userId, UserCase userRole, TimeZoneConfiguration configuration) { - TimeZoneCapabilities capabilities = createCapabilities(userId, userRole, configuration); + @UserIdInt int userId, UserCase userCase, TimeZoneConfiguration configuration) { + TimeZoneCapabilities capabilities = createCapabilities(userId, userCase, configuration); mFakeCallback.initializeUser(userId, capabilities, configuration); return this; } @@ -989,27 +1321,24 @@ public class TimeZoneDetectorStrategyImplTest { return this; } - Script simulateAutoTimeZoneDetectionEnabled(@UserIdInt int userId, boolean enabled) { - TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder() - .setAutoDetectionEnabled(enabled) - .build(); - return simulateUpdateConfiguration(userId, configuration); - } - /** - * Simulates the time zone detection strategy receiving an updated configuration. + * Simulates the time zone detection strategy receiving an updated configuration and checks + * the return value. */ Script simulateUpdateConfiguration( - @UserIdInt int userId, TimeZoneConfiguration configuration) { - mTimeZoneDetectorStrategy.updateConfiguration(userId, configuration); + @UserIdInt int userId, TimeZoneConfiguration configuration, + boolean expectedResult) { + assertEquals(expectedResult, + mTimeZoneDetectorStrategy.updateConfiguration(userId, configuration)); return this; } /** - * Simulates the time zone detection strategy receiving a telephony-originated suggestion. + * Simulates the time zone detection strategy receiving a geolocation-originated + * suggestion. */ - Script simulateTelephonyTimeZoneSuggestion(TelephonyTimeZoneSuggestion timeZoneSuggestion) { - mTimeZoneDetectorStrategy.suggestTelephonyTimeZone(timeZoneSuggestion); + Script simulateGeolocationTimeZoneSuggestion(GeolocationTimeZoneSuggestion suggestion) { + mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(suggestion); return this; } @@ -1024,13 +1353,26 @@ public class TimeZoneDetectorStrategyImplTest { return this; } + /** + * Simulates the time zone detection strategy receiving a telephony-originated suggestion. + */ + Script simulateTelephonyTimeZoneSuggestion(TelephonyTimeZoneSuggestion timeZoneSuggestion) { + mTimeZoneDetectorStrategy.suggestTelephonyTimeZone(timeZoneSuggestion); + return this; + } + + /** + * Confirms that the device's time zone has not been set by previous actions since the test + * state was last reset. + */ Script verifyTimeZoneNotChanged() { mFakeCallback.assertTimeZoneNotChanged(); return this; } - Script verifyTimeZoneChangedAndReset(TelephonyTimeZoneSuggestion suggestion) { - mFakeCallback.assertTimeZoneChangedTo(suggestion.getZoneId()); + /** Verifies the device's time zone has been set and clears change tracking history. */ + Script verifyTimeZoneChangedAndReset(String zoneId) { + mFakeCallback.assertTimeZoneChangedTo(zoneId); mFakeCallback.commitAllChanges(); return this; } @@ -1041,6 +1383,12 @@ public class TimeZoneDetectorStrategyImplTest { return this; } + Script verifyTimeZoneChangedAndReset(TelephonyTimeZoneSuggestion suggestion) { + mFakeCallback.assertTimeZoneChangedTo(suggestion.getZoneId()); + mFakeCallback.commitAllChanges(); + return this; + } + /** * Verifies that the configuration has been changed to the expected value. */ @@ -1120,4 +1468,8 @@ public class TimeZoneDetectorStrategyImplTest { mOnConfigurationChangedCalled = false; } } + + private static <T> List<T> list(T... values) { + return Arrays.asList(values); + } } diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java index f9343236662b..7b07102356f0 100644 --- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java @@ -74,7 +74,7 @@ public class TunerResourceManagerServiceTest { mReclaimed = true; } - public boolean isRelaimed() { + public boolean isReclaimed() { return mReclaimed; } } @@ -387,13 +387,13 @@ public class TunerResourceManagerServiceTest { new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT); assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isFalse(); - assertThat(listener.isRelaimed()).isFalse(); + assertThat(listener.isReclaimed()).isFalse(); request = new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS); assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isFalse(); - assertThat(listener.isRelaimed()).isFalse(); + assertThat(listener.isReclaimed()).isFalse(); } @Test @@ -452,7 +452,7 @@ public class TunerResourceManagerServiceTest { .getOwnerClientId()).isEqualTo(clientId1[0]); assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId()) .getOwnerClientId()).isEqualTo(clientId1[0]); - assertThat(listener.isRelaimed()).isTrue(); + assertThat(listener.isReclaimed()).isTrue(); } @Test @@ -486,7 +486,7 @@ public class TunerResourceManagerServiceTest { // Release frontend mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService - .getFrontendResource(frontendId)); + .getFrontendResource(frontendId), clientId[0]); assertThat(mTunerResourceManagerService .getFrontendResource(frontendId).isInUse()).isFalse(); assertThat(mTunerResourceManagerService @@ -548,7 +548,7 @@ public class TunerResourceManagerServiceTest { assertThat(mTunerResourceManagerService.getCasResource(1) .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId1[0]))); assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse(); - assertThat(listener.isRelaimed()).isTrue(); + assertThat(listener.isReclaimed()).isTrue(); } @Test @@ -633,7 +633,7 @@ public class TunerResourceManagerServiceTest { .isInUse()).isTrue(); assertThat(mTunerResourceManagerService.getLnbResource(lnbIds[0]) .getOwnerClientId()).isEqualTo(clientId1[0]); - assertThat(listener.isRelaimed()).isTrue(); + assertThat(listener.isReclaimed()).isTrue(); assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) .getInUseLnbIds().size()).isEqualTo(0); } @@ -761,4 +761,293 @@ public class TunerResourceManagerServiceTest { backgroundRecordProfile)).isEqualTo( (backgroundPlaybackPriority > backgroundRecordPriority)); } + + @Test + public void shareFrontendTest_FrontendWithExclusiveGroupReadyToShare() { + /**** Register Clients and Set Priority ****/ + + // Int array to save the returned client ids + int[] ownerClientId0 = new int[1]; + int[] ownerClientId1 = new int[1]; + int[] shareClientId0 = new int[1]; + int[] shareClientId1 = new int[1]; + + // Predefined client profiles + ResourceClientProfile[] ownerProfiles = new ResourceClientProfile[2]; + ResourceClientProfile[] shareProfiles = new ResourceClientProfile[2]; + ownerProfiles[0] = new ResourceClientProfile( + "0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE); + ownerProfiles[1] = new ResourceClientProfile( + "1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE); + shareProfiles[0] = new ResourceClientProfile( + "2" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD); + shareProfiles[1] = new ResourceClientProfile( + "3" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD); + + // Predefined client reclaim listeners + TestResourcesReclaimListener ownerListener0 = new TestResourcesReclaimListener(); + TestResourcesReclaimListener shareListener0 = new TestResourcesReclaimListener(); + TestResourcesReclaimListener ownerListener1 = new TestResourcesReclaimListener(); + TestResourcesReclaimListener shareListener1 = new TestResourcesReclaimListener(); + // Register clients and validate the returned client ids + mTunerResourceManagerService + .registerClientProfileInternal(ownerProfiles[0], ownerListener0, ownerClientId0); + mTunerResourceManagerService + .registerClientProfileInternal(shareProfiles[0], shareListener0, shareClientId0); + mTunerResourceManagerService + .registerClientProfileInternal(ownerProfiles[1], ownerListener1, ownerClientId1); + mTunerResourceManagerService + .registerClientProfileInternal(shareProfiles[1], shareListener1, shareClientId1); + assertThat(ownerClientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + assertThat(shareClientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + assertThat(ownerClientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + assertThat(shareClientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + + mTunerResourceManagerService.updateClientPriorityInternal( + ownerClientId0[0], + 100/*priority*/, + 0/*niceValue*/); + mTunerResourceManagerService.updateClientPriorityInternal( + shareClientId0[0], + 200/*priority*/, + 0/*niceValue*/); + mTunerResourceManagerService.updateClientPriorityInternal( + ownerClientId1[0], + 300/*priority*/, + 0/*niceValue*/); + mTunerResourceManagerService.updateClientPriorityInternal( + shareClientId1[0], + 400/*priority*/, + 0/*niceValue*/); + + /**** Init Frontend Resources ****/ + + // Predefined frontend info + TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; + infos[0] = new TunerFrontendInfo( + 0 /*id*/, + FrontendSettings.TYPE_DVBT, + 1 /*exclusiveGroupId*/); + infos[1] = new TunerFrontendInfo( + 1 /*id*/, + FrontendSettings.TYPE_DVBS, + 1 /*exclusiveGroupId*/); + + /**** Init Lnb Resources ****/ + int[] lnbIds = {1}; + mTunerResourceManagerService.setLnbInfoListInternal(lnbIds); + + // Update frontend list in TRM + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + /**** Request Frontend ****/ + + // Predefined frontend request and array to save returned frontend handle + int[] frontendHandle = new int[1]; + TunerFrontendRequest request = new TunerFrontendRequest( + ownerClientId0[0] /*clientId*/, + FrontendSettings.TYPE_DVBT); + + // Request call and validate granted resource and internal mapping + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)) + .isTrue(); + assertThat(mTunerResourceManagerService + .getResourceIdFromHandle(frontendHandle[0])) + .isEqualTo(infos[0].getId()); + assertThat(mTunerResourceManagerService + .getClientProfile(ownerClientId0[0]) + .getInUseFrontendIds()) + .isEqualTo(new HashSet<Integer>(Arrays.asList( + infos[0].getId(), + infos[1].getId()))); + + /**** Share Frontend ****/ + + // Share frontend call and validate the internal mapping + mTunerResourceManagerService.shareFrontendInternal( + shareClientId0[0]/*selfClientId*/, + ownerClientId0[0]/*targetClientId*/); + mTunerResourceManagerService.shareFrontendInternal( + shareClientId1[0]/*selfClientId*/, + ownerClientId0[0]/*targetClientId*/); + // Verify fe in use status + assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId()) + .isInUse()).isTrue(); + assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId()) + .isInUse()).isTrue(); + // Verify fe owner status + assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId()) + .getOwnerClientId()).isEqualTo(ownerClientId0[0]); + assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId()) + .getOwnerClientId()).isEqualTo(ownerClientId0[0]); + // Verify share fe client status in the primary owner client + assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0]) + .getShareFeClientIds()) + .isEqualTo(new HashSet<Integer>(Arrays.asList( + shareClientId0[0], + shareClientId1[0]))); + // Verify in use frontend list in all the primary owner and share owner clients + assertThat(mTunerResourceManagerService + .getClientProfile(ownerClientId0[0]) + .getInUseFrontendIds()) + .isEqualTo(new HashSet<Integer>(Arrays.asList( + infos[0].getId(), + infos[1].getId()))); + assertThat(mTunerResourceManagerService + .getClientProfile(shareClientId0[0]) + .getInUseFrontendIds()) + .isEqualTo(new HashSet<Integer>(Arrays.asList( + infos[0].getId(), + infos[1].getId()))); + assertThat(mTunerResourceManagerService + .getClientProfile(shareClientId1[0]) + .getInUseFrontendIds()) + .isEqualTo(new HashSet<Integer>(Arrays.asList( + infos[0].getId(), + infos[1].getId()))); + + /**** Remove Frontend Share Owner ****/ + + // Unregister the second share fe client + mTunerResourceManagerService.unregisterClientProfileInternal(shareClientId1[0]); + + // Validate the internal mapping + assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0]) + .getShareFeClientIds()) + .isEqualTo(new HashSet<Integer>(Arrays.asList( + shareClientId0[0]))); + assertThat(mTunerResourceManagerService + .getClientProfile(ownerClientId0[0]) + .getInUseFrontendIds()) + .isEqualTo(new HashSet<Integer>(Arrays.asList( + infos[0].getId(), + infos[1].getId()))); + assertThat(mTunerResourceManagerService + .getClientProfile(shareClientId0[0]) + .getInUseFrontendIds()) + .isEqualTo(new HashSet<Integer>(Arrays.asList( + infos[0].getId(), + infos[1].getId()))); + + /**** Request Shared Frontend with Higher Priority Client ****/ + + // Predefined second frontend request + request = new TunerFrontendRequest( + ownerClientId1[0] /*clientId*/, + FrontendSettings.TYPE_DVBT); + + // Second request call + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)) + .isTrue(); + + // Validate granted resource and internal mapping + assertThat(mTunerResourceManagerService + .getResourceIdFromHandle(frontendHandle[0])) + .isEqualTo(infos[0].getId()); + assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId()) + .getOwnerClientId()).isEqualTo(ownerClientId1[0]); + assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId()) + .getOwnerClientId()).isEqualTo(ownerClientId1[0]); + assertThat(mTunerResourceManagerService + .getClientProfile(ownerClientId1[0]) + .getInUseFrontendIds()) + .isEqualTo(new HashSet<Integer>(Arrays.asList( + infos[0].getId(), + infos[1].getId()))); + assertThat(mTunerResourceManagerService + .getClientProfile(ownerClientId0[0]) + .getInUseFrontendIds() + .isEmpty()) + .isTrue(); + assertThat(mTunerResourceManagerService + .getClientProfile(shareClientId0[0]) + .getInUseFrontendIds() + .isEmpty()) + .isTrue(); + assertThat(mTunerResourceManagerService + .getClientProfile(ownerClientId0[0]) + .getShareFeClientIds() + .isEmpty()) + .isTrue(); + assertThat(ownerListener0.isReclaimed()).isTrue(); + assertThat(shareListener0.isReclaimed()).isTrue(); + + /**** Release Frontend Resource From Primary Owner ****/ + + // Reshare the frontend + mTunerResourceManagerService.shareFrontendInternal( + shareClientId0[0]/*selfClientId*/, + ownerClientId1[0]/*targetClientId*/); + + // Release the frontend resource from the primary owner + mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService + .getFrontendResource(infos[0].getId()), ownerClientId1[0]); + + // Validate the internal mapping + assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId()) + .isInUse()).isFalse(); + assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId()) + .isInUse()).isFalse(); + // Verify client status + assertThat(mTunerResourceManagerService + .getClientProfile(ownerClientId1[0]) + .getInUseFrontendIds() + .isEmpty()) + .isTrue(); + assertThat(mTunerResourceManagerService + .getClientProfile(shareClientId0[0]) + .getInUseFrontendIds() + .isEmpty()) + .isTrue(); + assertThat(mTunerResourceManagerService + .getClientProfile(ownerClientId1[0]) + .getShareFeClientIds() + .isEmpty()) + .isTrue(); + + /**** Unregister Primary Owner when the Share owner owns an Lnb ****/ + + // Predefined Lnb request and handle array + TunerLnbRequest requestLnb = new TunerLnbRequest(shareClientId0[0]); + int[] lnbHandle = new int[1]; + + // Request for an Lnb + assertThat(mTunerResourceManagerService + .requestLnbInternal(requestLnb, lnbHandle)) + .isTrue(); + + // Request and share the frontend resource again + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)) + .isTrue(); + mTunerResourceManagerService.shareFrontendInternal( + shareClientId0[0]/*selfClientId*/, + ownerClientId1[0]/*targetClientId*/); + + // Unregister the primary owner of the shared frontend + mTunerResourceManagerService.unregisterClientProfileInternal(ownerClientId1[0]); + + // Validate the internal mapping + assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId()) + .isInUse()).isFalse(); + assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId()) + .isInUse()).isFalse(); + // Verify client status + assertThat(mTunerResourceManagerService + .getClientProfile(shareClientId0[0]) + .getInUseFrontendIds() + .isEmpty()) + .isTrue(); + assertThat(mTunerResourceManagerService + .getClientProfile(shareClientId0[0]) + .getInUseLnbIds()) + .isEqualTo(new HashSet<Integer>(Arrays.asList( + lnbIds[0]))); + } } diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 4dec7a1a0ab9..7c7b1a296673 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -1383,7 +1383,7 @@ public class AppStandbyControllerTests { getStandbyBucket(mController, PACKAGE_1)); mController.postReportSyncScheduled(PACKAGE_1, USER_ID, false); - mStateChangedLatch.await(100, TimeUnit.MILLISECONDS); + mStateChangedLatch.await(1000, TimeUnit.MILLISECONDS); assertEquals("Unexempted sync scheduled should bring the package out of the Never bucket", STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1)); @@ -1391,7 +1391,7 @@ public class AppStandbyControllerTests { mStateChangedLatch = new CountDownLatch(1); mController.postReportSyncScheduled(PACKAGE_1, USER_ID, false); - mStateChangedLatch.await(100, TimeUnit.MILLISECONDS); + mStateChangedLatch.await(1000, TimeUnit.MILLISECONDS); assertEquals("Unexempted sync scheduled should not elevate a non Never bucket", STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); } @@ -1402,7 +1402,7 @@ public class AppStandbyControllerTests { mInjector.mDeviceIdleMode = true; mStateChangedLatch = new CountDownLatch(1); mController.postReportSyncScheduled(PACKAGE_1, USER_ID, true); - mStateChangedLatch.await(100, TimeUnit.MILLISECONDS); + mStateChangedLatch.await(1000, TimeUnit.MILLISECONDS); assertEquals("Exempted sync scheduled in doze should set bucket to working set", STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1)); @@ -1410,7 +1410,7 @@ public class AppStandbyControllerTests { mInjector.mDeviceIdleMode = false; mStateChangedLatch = new CountDownLatch(1); mController.postReportSyncScheduled(PACKAGE_1, USER_ID, true); - mStateChangedLatch.await(100, TimeUnit.MILLISECONDS); + mStateChangedLatch.await(1000, TimeUnit.MILLISECONDS); assertEquals("Exempted sync scheduled while not in doze should set bucket to active", STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1)); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 5b2d738eb760..9319bea497fb 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3886,7 +3886,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.updateUriPermissions(recordB, recordA, mContext.getPackageName(), USER_SYSTEM); verify(mUgmInternal, times(1)).revokeUriPermissionFromOwner(any(), - eq(message1.getDataUri()), anyInt(), anyInt()); + eq(message1.getDataUri()), anyInt(), anyInt(), eq(null), eq(-1)); // Update back means we grant access to first again reset(mUgm); diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java deleted file mode 100644 index f6c854e23494..000000000000 --- a/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.slice; - -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; - -import androidx.test.filters.SmallTest; - -import com.android.server.UiServiceTestCase; -import com.android.server.slice.SliceManagerService.PackageMatchingCache; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.function.Supplier; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public class PackageMatchingCacheTest extends UiServiceTestCase { - - private final Supplier<String> supplier = mock(Supplier.class); - private final PackageMatchingCache cache = new PackageMatchingCache(supplier); - - @Test - public void testNulls() { - // Doesn't get for a null input - cache.matches(null); - verify(supplier, never()).get(); - - // Gets once valid input in sent. - cache.matches(""); - verify(supplier).get(); - } - - @Test - public void testCaching() { - when(supplier.get()).thenReturn("ret.pkg"); - - assertTrue(cache.matches("ret.pkg")); - assertTrue(cache.matches("ret.pkg")); - assertTrue(cache.matches("ret.pkg")); - - verify(supplier, times(1)).get(); - } - - @Test - public void testGetOnFailure() { - when(supplier.get()).thenReturn("ret.pkg"); - assertTrue(cache.matches("ret.pkg")); - - when(supplier.get()).thenReturn("other.pkg"); - assertTrue(cache.matches("other.pkg")); - verify(supplier, times(2)).get(); - } -} diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java index a4436951f48b..cf1c36c0d243 100644 --- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java @@ -90,8 +90,6 @@ public class SliceManagerServiceTest extends UiServiceTestCase { @Test public void testAddPinCreatesPinned() throws RemoteException { - doReturn("pkg").when(mService).getDefaultHome(anyInt()); - mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); verify(mService, times(1)).createPinnedSlice(eq(maybeAddUserId(TEST_URI, 0)), anyString()); @@ -99,8 +97,6 @@ public class SliceManagerServiceTest extends UiServiceTestCase { @Test public void testRemovePinDestroysPinned() throws RemoteException { - doReturn("pkg").when(mService).getDefaultHome(anyInt()); - mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); when(mCreatedSliceState.unpin(eq("pkg"), eq(mToken))).thenReturn(false); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index 46c3e22da38c..eb78172cd562 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -20,6 +20,7 @@ import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.content.ComponentName.createRelative; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions; @@ -176,7 +177,8 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { public void testOnActivityLaunchCancelled_hasDrawn() { onActivityLaunched(mTopActivity); - mTopActivity.mVisibleRequested = mTopActivity.mDrawn = true; + mTopActivity.mVisibleRequested = true; + doReturn(true).when(mTopActivity).isReportedDrawn(); // Cannot time already-visible activities. notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity); @@ -187,16 +189,14 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { @Test public void testOnActivityLaunchCancelled_finishedBeforeDrawn() { - mTopActivity.mVisibleRequested = mTopActivity.mDrawn = true; + mTopActivity.mVisibleRequested = true; + doReturn(true).when(mTopActivity).isReportedDrawn(); - // Suppress resume when creating the record because we want to notify logger manually. - mSupervisor.beginDeferResume(); // Create an activity with different process that meets process switch. final ActivityRecord noDrawnActivity = new ActivityBuilder(mAtm) .setTask(mTopActivity.getTask()) .setProcessName("other") .build(); - mSupervisor.readyToResume(); notifyActivityLaunching(noDrawnActivity.intent); notifyActivityLaunched(START_SUCCESS, noDrawnActivity); @@ -294,7 +294,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { public void testOnActivityLaunchCancelledTrampoline() { onActivityLaunchedTrampoline(); - mTopActivity.mDrawn = true; + doReturn(true).when(mTopActivity).isReportedDrawn(); // Cannot time already-visible activities. notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java index 96b970056c98..27e2d1370fae 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java @@ -22,6 +22,7 @@ import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; @@ -38,13 +39,8 @@ import static org.mockito.ArgumentMatchers.eq; import android.app.WaitResult; import android.content.pm.ActivityInfo; -import android.graphics.PixelFormat; -import android.hardware.display.DisplayManager; -import android.hardware.display.VirtualDisplay; -import android.media.ImageReader; import android.platform.test.annotations.Presubmit; import android.view.Display; -import android.view.DisplayInfo; import androidx.test.filters.MediumTest; @@ -180,49 +176,28 @@ public class ActivityStackSupervisorTests extends WindowTestsBase { eq(true) /* focused */); } + /** + * Ensures that a trusted display can launch arbitrary activity and an untrusted display can't. + */ @Test - /** Ensures that a trusted virtual display can launch arbitrary activities. */ - public void testTrustedVirtualDisplayCanLaunchActivities() { - final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP); - final Task stack = new StackBuilder(mRootWindowContainer) - .setDisplay(newDisplay).build(); - final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity(); - VirtualDisplay virtualDisplay = createVirtualDisplay(true); - final boolean allowed = mSupervisor.isCallerAllowedToLaunchOnDisplay(1234, 1234, - virtualDisplay.getDisplay().getDisplayId(), unresizableActivity.info); + public void testDisplayCanLaunchActivities() { + final Display display = mDisplayContent.mDisplay; + // An empty info without FLAG_ALLOW_EMBEDDED. + final ActivityInfo activityInfo = new ActivityInfo(); + final int callingPid = 12345; + final int callingUid = 12345; + spyOn(display); - assertThat(allowed).isTrue(); - } + doReturn(true).when(display).isTrusted(); + final boolean allowedOnTrusted = mSupervisor.isCallerAllowedToLaunchOnDisplay(callingPid, + callingUid, display.getDisplayId(), activityInfo); - @Test - /** Ensures that an untrusted virtual display cannot launch arbitrary activities. */ - public void testUntrustedVirtualDisplayCannotLaunchActivities() { - final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP); - final Task stack = new StackBuilder(mRootWindowContainer) - .setDisplay(newDisplay).build(); - final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity(); - VirtualDisplay virtualDisplay = createVirtualDisplay(false); - final boolean allowed = mSupervisor.isCallerAllowedToLaunchOnDisplay(1234, 1234, - virtualDisplay.getDisplay().getDisplayId(), unresizableActivity.info); + assertThat(allowedOnTrusted).isTrue(); - assertThat(allowed).isFalse(); - } + doReturn(false).when(display).isTrusted(); + final boolean allowedOnUntrusted = mSupervisor.isCallerAllowedToLaunchOnDisplay(callingPid, + callingUid, display.getDisplayId(), activityInfo); - private VirtualDisplay createVirtualDisplay(boolean trusted) { - final DisplayManager dm = mContext.getSystemService(DisplayManager.class); - final DisplayInfo displayInfo = new DisplayInfo(); - final Display defaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY); - defaultDisplay.getDisplayInfo(displayInfo); - int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; - if (trusted) { - flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; - } - - final ImageReader imageReader = ImageReader.newInstance( - displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2); - - return dm.createVirtualDisplay("virtualDisplay", displayInfo.logicalWidth, - displayInfo.logicalHeight, - displayInfo.logicalDensityDpi, imageReader.getSurface(), flags); + assertThat(allowedOnUntrusted).isFalse(); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java b/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java index 8223024234fb..578a43cba33d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java @@ -38,13 +38,13 @@ import org.junit.Test; */ @SmallTest @Presubmit -@Ignore("b/163095037") public class ProtoLogIntegrationTest { @After public void tearDown() { ProtoLogImpl.setSingleInstance(null); } + @Ignore("b/163095037") @Test public void testProtoLogToolIntegration() { ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index 1ec9bd24ad59..b89d16807a6e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -890,8 +890,8 @@ public class RootActivityContainerTests extends WindowTestsBase { // Make sure the root task is valid and can be reused on default display. final Task stack = mRootWindowContainer.getValidLaunchStackInTaskDisplayArea( - mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task, null, - null); + mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task, + null /* options */, null /* launchParams */); assertEquals(task, stack); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index 2f736555ae1b..189e540af0ca 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.Display.INVALID_DISPLAY; @@ -253,6 +255,19 @@ public class WindowProcessControllerTests extends WindowTestsBase { assertFalse(mWpc.registeredForActivityConfigChanges()); } + @Test + public void testProcessLevelConfiguration() { + Configuration config = new Configuration(); + config.windowConfiguration.setActivityType(ACTIVITY_TYPE_HOME); + mWpc.onRequestedOverrideConfigurationChanged(config); + assertEquals(ACTIVITY_TYPE_HOME, config.windowConfiguration.getActivityType()); + assertEquals(ACTIVITY_TYPE_UNDEFINED, mWpc.getActivityType()); + + mWpc.onMergedOverrideConfigurationChanged(config); + assertEquals(ACTIVITY_TYPE_HOME, config.windowConfiguration.getActivityType()); + assertEquals(ACTIVITY_TYPE_UNDEFINED, mWpc.getActivityType()); + } + private TestDisplayContent createTestDisplayContentInContainer() { return new TestDisplayContent.Builder(mAtm, 1000, 1500).build(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index d9c48fc4ba37..e0785c13a336 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -27,6 +27,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -145,29 +146,40 @@ public class WindowTokenTests extends WindowTestsBase { assertEquals(0, token.getWindowsCount()); } - @UseTestDisplay(addWindows = { W_ACTIVITY, W_WALLPAPER }) @Test public void testFinishFixedRotationTransform() { - final WindowToken appToken = mAppWindow.mToken; - final WindowToken wallpaperToken = mWallpaperWindow.mToken; + final WindowToken[] tokens = new WindowToken[3]; + for (int i = 0; i < tokens.length; i++) { + tokens[i] = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent); + } + final Configuration config = new Configuration(mDisplayContent.getConfiguration()); final int originalRotation = config.windowConfiguration.getRotation(); final int targetRotation = (originalRotation + 1) % 4; config.windowConfiguration.setRotation(targetRotation); - appToken.applyFixedRotationTransform(mDisplayInfo, mDisplayContent.mDisplayFrames, config); - wallpaperToken.linkFixedRotationTransform(appToken); + tokens[0].applyFixedRotationTransform(mDisplayInfo, mDisplayContent.mDisplayFrames, config); + tokens[1].linkFixedRotationTransform(tokens[0]); // The window tokens should apply the rotation by the transformation. - assertEquals(targetRotation, appToken.getWindowConfiguration().getRotation()); - assertEquals(targetRotation, wallpaperToken.getWindowConfiguration().getRotation()); + assertEquals(targetRotation, tokens[0].getWindowConfiguration().getRotation()); + assertEquals(targetRotation, tokens[1].getWindowConfiguration().getRotation()); + + tokens[2].applyFixedRotationTransform(mDisplayInfo, mDisplayContent.mDisplayFrames, config); + // The tokens[1] was linked to tokens[0], this should make tokens[1] link to tokens[2]. + tokens[1].linkFixedRotationTransform(tokens[2]); + + // Assume the display doesn't rotate, the transformation will be canceled. + tokens[0].finishFixedRotationTransform(); - // The display doesn't rotate, the transformation will be canceled. - mAppWindow.mToken.finishFixedRotationTransform(); + // The tokens[0] should restore to the original rotation. + assertEquals(originalRotation, tokens[0].getWindowConfiguration().getRotation()); + // The tokens[1] is linked to tokens[2], it should keep the target rotation. + assertNotEquals(originalRotation, tokens[1].getWindowConfiguration().getRotation()); - // The window tokens should restore to the original rotation. - assertEquals(originalRotation, appToken.getWindowConfiguration().getRotation()); - assertEquals(originalRotation, wallpaperToken.getWindowConfiguration().getRotation()); + tokens[2].finishFixedRotationTransform(); + // The rotation of tokens[1] should be restored because its linked state is finished. + assertEquals(originalRotation, tokens[1].getWindowConfiguration().getRotation()); } /** diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 78556ef41edb..1e5d92b270d2 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -61,7 +61,6 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ShortcutServiceInternal; -import android.content.pm.UserInfo; import android.content.res.Configuration; import android.os.Binder; import android.os.Environment; @@ -306,25 +305,25 @@ public class UsageStatsService extends SystemService implements @Override public void onUserStopping(@NonNull TargetUser user) { - final UserInfo userInfo = user.getUserInfo(); + final int userId = user.getUserIdentifier(); synchronized (mLock) { // User was started but never unlocked so no need to report a user stopped event - if (!mUserUnlockedStates.get(userInfo.id)) { - persistPendingEventsLocked(userInfo.id); + if (!mUserUnlockedStates.get(userId)) { + persistPendingEventsLocked(userId); return; } // Report a user stopped event before persisting all stats to disk via the user service final Event event = new Event(USER_STOPPED, SystemClock.elapsedRealtime()); event.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME; - reportEvent(event, userInfo.id); - final UserUsageStatsService userService = mUserState.get(userInfo.id); + reportEvent(event, userId); + final UserUsageStatsService userService = mUserState.get(userId); if (userService != null) { userService.userStopped(); } - mUserUnlockedStates.put(userInfo.id, false); - mUserState.put(userInfo.id, null); // release the service (mainly for GC) + mUserUnlockedStates.put(userId, false); + mUserState.put(userId, null); // release the service (mainly for GC) } } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java index 0ea84da54b60..26d46dbd2ab7 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java @@ -1276,7 +1276,8 @@ public class SoundTriggerService extends SystemService { * @return The initialized AudioRecord */ private @NonNull AudioRecord createAudioRecordForEvent( - @NonNull SoundTrigger.GenericRecognitionEvent event) { + @NonNull SoundTrigger.GenericRecognitionEvent event) + throws IllegalArgumentException, UnsupportedOperationException { AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder(); attributesBuilder.setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD); AudioAttributes attributes = attributesBuilder.build(); @@ -1285,21 +1286,15 @@ public class SoundTriggerService extends SystemService { sEventLogger.log(new SoundTriggerLogger.StringEvent("createAudioRecordForEvent")); - try { - return (new AudioRecord.Builder()) - .setAudioAttributes(attributes) - .setAudioFormat((new AudioFormat.Builder()) - .setChannelMask(originalFormat.getChannelMask()) - .setEncoding(originalFormat.getEncoding()) - .setSampleRate(originalFormat.getSampleRate()) - .build()) - .setSessionId(event.getCaptureSession()) - .build(); - } catch (IllegalArgumentException | UnsupportedOperationException e) { - Slog.w(TAG, mPuuid + ": createAudioRecordForEvent(" + event - + "), failed to create AudioRecord"); - return null; - } + return (new AudioRecord.Builder()) + .setAudioAttributes(attributes) + .setAudioFormat((new AudioFormat.Builder()) + .setChannelMask(originalFormat.getChannelMask()) + .setEncoding(originalFormat.getEncoding()) + .setSampleRate(originalFormat.getSampleRate()) + .build()) + .setSessionId(event.getCaptureSession()) + .build(); } @Override @@ -1325,13 +1320,13 @@ public class SoundTriggerService extends SystemService { // execute if throttled: () -> { if (event.isCaptureAvailable()) { - AudioRecord capturedData = createAudioRecordForEvent(event); - - // Currently we need to start and release the audio record to reset - // the DSP even if we don't want to process the event - if (capturedData != null) { + try { + AudioRecord capturedData = createAudioRecordForEvent(event); capturedData.startRecording(); capturedData.release(); + } catch (IllegalArgumentException | UnsupportedOperationException e) { + Slog.w(TAG, mPuuid + ": createAudioRecordForEvent(" + event + + "), failed to create AudioRecord"); } } })); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 0f898f8e679e..a2215ceed36a 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -164,13 +164,13 @@ public class VoiceInteractionManagerService extends SystemService { } } - private boolean isSupported(UserInfo user) { + @Override + public boolean isUserSupported(@NonNull TargetUser user) { return user.isFull(); } - @Override - public boolean isUserSupported(TargetUser user) { - return isSupported(user.getUserInfo()); + private boolean isUserSupported(@NonNull UserInfo user) { + return user.isFull(); } @Override @@ -461,7 +461,7 @@ public class VoiceInteractionManagerService extends SystemService { private void setCurrentUserLocked(@UserIdInt int userHandle) { mCurUser = userHandle; final UserInfo userInfo = mUserManagerInternal.getUserInfo(mCurUser); - mCurUserSupported = isSupported(userInfo); + mCurUserSupported = isUserSupported(userInfo); } public void switchUser(@UserIdInt int userHandle) { @@ -967,10 +967,10 @@ public class VoiceInteractionManagerService extends SystemService { throw new IllegalArgumentException("Illegal argument(s) in getKeyphraseSoundModel"); } - final int callingUid = UserHandle.getCallingUserId(); + final int callingUserId = UserHandle.getCallingUserId(); final long caller = Binder.clearCallingIdentity(); try { - return mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale); + return mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUserId, bcp47Locale); } finally { Binder.restoreCallingIdentity(caller); } @@ -1010,7 +1010,7 @@ public class VoiceInteractionManagerService extends SystemService { "Illegal argument(s) in deleteKeyphraseSoundModel"); } - final int callingUid = UserHandle.getCallingUserId(); + final int callingUserId = UserHandle.getCallingUserId(); final long caller = Binder.clearCallingIdentity(); boolean deleted = false; try { @@ -1018,7 +1018,8 @@ public class VoiceInteractionManagerService extends SystemService { if (unloadStatus != SoundTriggerInternal.STATUS_OK) { Slog.w(TAG, "Unable to unload keyphrase sound model:" + unloadStatus); } - deleted = mDbHelper.deleteKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale); + deleted = mDbHelper.deleteKeyphraseSoundModel( + keyphraseId, callingUserId, bcp47Locale); return deleted ? SoundTriggerInternal.STATUS_OK : SoundTriggerInternal.STATUS_ERROR; } finally { if (deleted) { @@ -1045,11 +1046,11 @@ public class VoiceInteractionManagerService extends SystemService { throw new IllegalArgumentException("Illegal argument(s) in isEnrolledForKeyphrase"); } - final int callingUid = UserHandle.getCallingUserId(); + final int callingUserId = UserHandle.getCallingUserId(); final long caller = Binder.clearCallingIdentity(); try { KeyphraseSoundModel model = - mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale); + mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUserId, bcp47Locale); return model != null; } finally { Binder.restoreCallingIdentity(caller); @@ -1067,11 +1068,11 @@ public class VoiceInteractionManagerService extends SystemService { throw new IllegalArgumentException("Illegal argument(s) in isEnrolledForKeyphrase"); } - final int callingUid = UserHandle.getCallingUserId(); + final int callingUserId = UserHandle.getCallingUserId(); final long caller = Binder.clearCallingIdentity(); try { KeyphraseSoundModel model = - mDbHelper.getKeyphraseSoundModel(keyphrase, callingUid, bcp47Locale); + mDbHelper.getKeyphraseSoundModel(keyphrase, callingUserId, bcp47Locale); if (model == null) { return null; } @@ -1118,11 +1119,11 @@ public class VoiceInteractionManagerService extends SystemService { } } - int callingUid = UserHandle.getCallingUserId(); + final int callingUserId = UserHandle.getCallingUserId(); final long caller = Binder.clearCallingIdentity(); try { KeyphraseSoundModel soundModel = - mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale); + mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUserId, bcp47Locale); if (soundModel == null || soundModel.getUuid() == null || soundModel.getKeyphrases() == null) { diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 3646647e9734..6288bc1698e9 100755 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -2488,6 +2488,42 @@ public abstract class ConnectionService extends Service { } /** + * Ask some other {@code ConnectionService} to create a {@code RemoteConference} given an + * incoming request. This is used by {@code ConnectionService}s that are registered with + * {@link PhoneAccount#CAPABILITY_ADHOC_CONFERENCE_CALLING}. + * + * @param connectionManagerPhoneAccount See description at + * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. + * @param request Details about the incoming conference call. + * @return The {@code RemoteConference} object to satisfy this call, or {@code null} to not + * handle the call. + */ + public final @Nullable RemoteConference createRemoteIncomingConference( + @Nullable PhoneAccountHandle connectionManagerPhoneAccount, + @Nullable ConnectionRequest request) { + return mRemoteConnectionManager.createRemoteConference(connectionManagerPhoneAccount, + request, true); + } + + /** + * Ask some other {@code ConnectionService} to create a {@code RemoteConference} given an + * outgoing request. This is used by {@code ConnectionService}s that are registered with + * {@link PhoneAccount#CAPABILITY_ADHOC_CONFERENCE_CALLING}. + * + * @param connectionManagerPhoneAccount See description at + * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. + * @param request Details about the outgoing conference call. + * @return The {@code RemoteConference} object to satisfy this call, or {@code null} to not + * handle the call. + */ + public final @Nullable RemoteConference createRemoteOutgoingConference( + @Nullable PhoneAccountHandle connectionManagerPhoneAccount, + @Nullable ConnectionRequest request) { + return mRemoteConnectionManager.createRemoteConference(connectionManagerPhoneAccount, + request, false); + } + + /** * Indicates to the relevant {@code RemoteConnectionService} that the specified * {@link RemoteConnection}s should be merged into a conference call. * <p> diff --git a/telecomm/java/android/telecom/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java index 502b7c01b0c0..e024e6186519 100644 --- a/telecomm/java/android/telecom/RemoteConference.java +++ b/telecomm/java/android/telecom/RemoteConference.java @@ -16,14 +16,14 @@ package android.telecom; -import com.android.internal.telecom.IConnectionService; - import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; +import com.android.internal.telecom.IConnectionService; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -155,6 +155,14 @@ public final class RemoteConference { } /** @hide */ + RemoteConference(DisconnectCause disconnectCause) { + mId = "NULL"; + mConnectionService = null; + mState = Connection.STATE_DISCONNECTED; + mDisconnectCause = disconnectCause; + } + + /** @hide */ String getId() { return mId; } @@ -583,4 +591,16 @@ public final class RemoteConference { } } } + + /** + * Create a {@link RemoteConference} represents a failure, and which will + * be in {@link Connection#STATE_DISCONNECTED}. + * + * @param disconnectCause The disconnect cause. + * @return a failed {@link RemoteConference} + * @hide + */ + public static RemoteConference failure(DisconnectCause disconnectCause) { + return new RemoteConference(disconnectCause); + } } diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java index df3362578680..52210a55c8d0 100644 --- a/telecomm/java/android/telecom/RemoteConnection.java +++ b/telecomm/java/android/telecom/RemoteConnection.java @@ -16,10 +16,6 @@ package android.telecom; -import com.android.internal.telecom.IConnectionService; -import com.android.internal.telecom.IVideoCallback; -import com.android.internal.telecom.IVideoProvider; - import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -33,6 +29,10 @@ import android.os.RemoteException; import android.telecom.Logging.Session; import android.view.Surface; +import com.android.internal.telecom.IConnectionService; +import com.android.internal.telecom.IVideoCallback; +import com.android.internal.telecom.IVideoProvider; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -1114,6 +1114,23 @@ public final class RemoteConnection { } /** + * Instructs this {@link RemoteConnection} to initiate a conference with a list of + * participants. + * <p> + * + * @param participants with which conference call will be formed. + */ + public void addConferenceParticipants(@NonNull List<Uri> participants) { + try { + if (mConnected) { + mConnectionService.addConferenceParticipants(mConnectionId, participants, + null /*Session.Info*/); + } + } catch (RemoteException ignored) { + } + } + + /** * Set the audio state of this {@code RemoteConnection}. * * @param state The audio state of this {@code RemoteConnection}. diff --git a/telecomm/java/android/telecom/RemoteConnectionManager.java b/telecomm/java/android/telecom/RemoteConnectionManager.java index 0322218d75dc..f3c7bd83ed4b 100644 --- a/telecomm/java/android/telecom/RemoteConnectionManager.java +++ b/telecomm/java/android/telecom/RemoteConnectionManager.java @@ -73,6 +73,37 @@ public class RemoteConnectionManager { return null; } + /** + * Ask a {@code RemoteConnectionService} to create a {@code RemoteConference}. + * @param connectionManagerPhoneAccount See description at + * {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. + * @param request Details about the incoming conference call. + * @param isIncoming {@code true} if it's an incoming conference. + * @return + */ + public RemoteConference createRemoteConference( + PhoneAccountHandle connectionManagerPhoneAccount, + ConnectionRequest request, + boolean isIncoming) { + PhoneAccountHandle accountHandle = request.getAccountHandle(); + if (accountHandle == null) { + throw new IllegalArgumentException("accountHandle must be specified."); + } + + ComponentName componentName = request.getAccountHandle().getComponentName(); + if (!mRemoteConnectionServices.containsKey(componentName)) { + throw new UnsupportedOperationException("accountHandle not supported: " + + componentName); + } + + RemoteConnectionService remoteService = mRemoteConnectionServices.get(componentName); + if (remoteService != null) { + return remoteService.createRemoteConference( + connectionManagerPhoneAccount, request, isIncoming); + } + return null; + } + public void conferenceRemoteConnections(RemoteConnection a, RemoteConnection b) { if (a.getConnectionService() == b.getConnectionService()) { try { diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java index a0833011715d..bf6a6ef793ff 100644 --- a/telecomm/java/android/telecom/RemoteConnectionService.java +++ b/telecomm/java/android/telecom/RemoteConnectionService.java @@ -31,9 +31,9 @@ import com.android.internal.telecom.RemoteServiceCallback; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; -import java.util.List; import java.util.UUID; /** @@ -591,6 +591,38 @@ final class RemoteConnectionService { } } + RemoteConference createRemoteConference( + PhoneAccountHandle connectionManagerPhoneAccount, + ConnectionRequest request, + boolean isIncoming) { + final String id = UUID.randomUUID().toString(); + try { + if (mConferenceById.isEmpty()) { + mOutgoingConnectionServiceRpc.addConnectionServiceAdapter(mServant.getStub(), + null /*Session.Info*/); + } + RemoteConference conference = new RemoteConference(id, mOutgoingConnectionServiceRpc); + mOutgoingConnectionServiceRpc.createConference(connectionManagerPhoneAccount, + id, + request, + isIncoming, + false /* isUnknownCall */, + null /*Session.info*/); + conference.registerCallback(new RemoteConference.Callback() { + @Override + public void onDestroyed(RemoteConference conference) { + mConferenceById.remove(id); + maybeDisconnectAdapter(); + } + }); + conference.putExtras(request.getExtras()); + return conference; + } catch (RemoteException e) { + return RemoteConference.failure( + new DisconnectCause(DisconnectCause.ERROR, e.toString())); + } + } + private boolean hasConnection(String callId) { return mConnectionById.containsKey(callId); } diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt index 09c16595e2c7..52e0953813a0 100644 --- a/telephony/api/system-current.txt +++ b/telephony/api/system-current.txt @@ -1625,6 +1625,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); + field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43 field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0 diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java index e57b03098758..d4308c4c30ad 100644 --- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java +++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java @@ -17,6 +17,7 @@ package com.android.internal.telephony; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -74,7 +75,7 @@ public final class CarrierAppUtils { * privileged apps may have changed. */ public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage, - TelephonyManager telephonyManager, int userId, Context context) { + TelephonyManager telephonyManager, @UserIdInt int userId, Context context) { if (DEBUG) { Log.d(TAG, "disableCarrierAppsUntilPrivileged"); } @@ -101,7 +102,7 @@ public final class CarrierAppUtils { * Manager can kill it, and this can lead to crashes as the app is in an unexpected state. */ public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage, - int userId, Context context) { + @UserIdInt int userId, Context context) { if (DEBUG) { Log.d(TAG, "disableCarrierAppsUntilPrivileged"); } @@ -117,9 +118,9 @@ public final class CarrierAppUtils { systemCarrierAssociatedAppsDisabledUntilUsed, context); } - private static ContentResolver getContentResolverForUser(Context context, int userId) { - Context userContext = context.createContextAsUser(UserHandle.getUserHandleForUid(userId), - 0); + private static ContentResolver getContentResolverForUser(Context context, + @UserIdInt int userId) { + Context userContext = context.createContextAsUser(UserHandle.of(userId), 0); return userContext.getContentResolver(); } diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java index e34bbfcde492..06c34dcf290a 100644 --- a/telephony/java/android/telephony/CellIdentityNr.java +++ b/telephony/java/android/telephony/CellIdentityNr.java @@ -37,7 +37,7 @@ public final class CellIdentityNr extends CellIdentity { private static final String TAG = "CellIdentityNr"; private static final int MAX_PCI = 1007; - private static final int MAX_TAC = 65535; + private static final int MAX_TAC = 16777215; // 0xffffff private static final int MAX_NRARFCN = 3279165; private static final long MAX_NCI = 68719476735L; @@ -53,7 +53,7 @@ public final class CellIdentityNr extends CellIdentity { /** * * @param pci Physical Cell Id in range [0, 1007]. - * @param tac 16-bit Tracking Area Code. + * @param tac 24-bit Tracking Area Code. * @param nrArfcn NR Absolute Radio Frequency Channel Number, in range [0, 3279165]. * @param bands Bands used by the cell. Band number defined in 3GPP TS 38.101-1 and TS 38.101-2. * @param mccStr 3-digit Mobile Country Code in string format. @@ -199,9 +199,9 @@ public final class CellIdentityNr extends CellIdentity { /** * Get the tracking area code. - * @return a 16 bit integer or {@link CellInfo#UNAVAILABLE} if unknown. + * @return a 24 bit integer or {@link CellInfo#UNAVAILABLE} if unknown. */ - @IntRange(from = 0, to = 65535) + @IntRange(from = 0, to = 16777215) public int getTac() { return mTac; } diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 2b2608724e12..78dc377a303c 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -46,6 +46,7 @@ import com.android.internal.telephony.IIntegerConsumer; import com.android.internal.telephony.ISms; import com.android.internal.telephony.ITelephony; import com.android.internal.telephony.SmsRawData; +import com.android.telephony.Rlog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -1953,7 +1954,6 @@ public final class SmsManager { public boolean enableCellBroadcastRange(int startMessageId, int endMessageId, @android.telephony.SmsCbMessage.MessageFormat int ranType) { boolean success = false; - if (endMessageId < startMessageId) { throw new IllegalArgumentException("endMessageId < startMessageId"); } @@ -1962,10 +1962,14 @@ public final class SmsManager { if (iSms != null) { // If getSubscriptionId() returns INVALID or an inactive subscription, we will use // the default phone internally. - success = iSms.enableCellBroadcastRangeForSubscriber(getSubscriptionId(), + int subId = getSubscriptionId(); + success = iSms.enableCellBroadcastRangeForSubscriber(subId, startMessageId, endMessageId, ranType); + Rlog.d(TAG, "enableCellBroadcastRange: " + (success ? "succeeded" : "failed") + + " at calling enableCellBroadcastRangeForSubscriber. subId = " + subId); } } catch (RemoteException ex) { + Rlog.d(TAG, "enableCellBroadcastRange: " + ex.getStackTrace()); // ignore it } @@ -2019,10 +2023,14 @@ public final class SmsManager { if (iSms != null) { // If getSubscriptionId() returns INVALID or an inactive subscription, we will use // the default phone internally. - success = iSms.disableCellBroadcastRangeForSubscriber(getSubscriptionId(), + int subId = getSubscriptionId(); + success = iSms.disableCellBroadcastRangeForSubscriber(subId, startMessageId, endMessageId, ranType); + Rlog.d(TAG, "disableCellBroadcastRange: " + (success ? "succeeded" : "failed") + + " at calling disableCellBroadcastRangeForSubscriber. subId = " + subId); } } catch (RemoteException ex) { + Rlog.d(TAG, "disableCellBroadcastRange: " + ex.getStackTrace()); // ignore it } diff --git a/telephony/java/android/telephony/ims/ImsConferenceState.java b/telephony/java/android/telephony/ims/ImsConferenceState.java index 21bef001efae..9bf2f44395c4 100644 --- a/telephony/java/android/telephony/ims/ImsConferenceState.java +++ b/telephony/java/android/telephony/ims/ImsConferenceState.java @@ -203,10 +203,10 @@ public final class ImsConferenceState implements Parcelable { for (String key : participantData.keySet()) { sb.append(key); sb.append("="); - if (ENDPOINT.equals(key) || USER.equals(key)) { - sb.append(Rlog.pii(TAG, participantData.get(key))); - } else { + if (STATUS.equals(key)) { sb.append(participantData.get(key)); + } else { + sb.append(Rlog.pii(TAG, participantData.get(key))); } sb.append(", "); } diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 1a606b7ae6a7..2a073a1f1d81 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -851,6 +851,19 @@ public class ProvisioningManager { public static final int KEY_RTT_ENABLED = 66; /** + * An obfuscated string defined by the carrier to indicate VoWiFi entitlement status. + * + * <p>Implementation note: how to generate the value and how it affects VoWiFi service + * should follow carrier requirements. For example, set an empty string could result in + * VoWiFi being disabled by IMS service, and set to a specific string could enable. + * + * <p>Value is in String format. + * @see #setProvisioningStringValue(int, String) + * @see #getProvisioningStringValue(int) + */ + public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; + + /** * Callback for IMS provisioning changes. */ public static class Callback { diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java index d0cec52dfc86..487786045b8e 100644 --- a/telephony/java/com/android/ims/ImsConfig.java +++ b/telephony/java/com/android/ims/ImsConfig.java @@ -729,7 +729,8 @@ public class ImsConfig { // Expand the operator config items as needed here, need to change // PROVISIONED_CONFIG_END after that. - public static final int PROVISIONED_CONFIG_END = RTT_SETTING_ENABLED; + public static final int PROVISIONED_CONFIG_END = + ProvisioningManager.KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID; // Expand the operator config items as needed here. } diff --git a/tests/AutoVerify/app1/Android.bp b/tests/AutoVerify/app1/Android.bp deleted file mode 100644 index 548519fa653b..000000000000 --- a/tests/AutoVerify/app1/Android.bp +++ /dev/null @@ -1,11 +0,0 @@ -android_app { - name: "AutoVerifyTest", - srcs: ["src/**/*.java"], - resource_dirs: ["res"], - platform_apis: true, - min_sdk_version: "26", - target_sdk_version: "26", - optimize: { - enabled: false, - }, -} diff --git a/tests/AutoVerify/app1/AndroidManifest.xml b/tests/AutoVerify/app1/AndroidManifest.xml deleted file mode 100644 index d9caad490d82..000000000000 --- a/tests/AutoVerify/app1/AndroidManifest.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2020 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. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.test.autoverify" > - - <uses-sdk android:targetSdkVersion="26" /> - - <application - android:label="@string/app_name" > - <activity - android:name=".MainActivity" - android:label="@string/app_name" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - - <intent-filter android:autoVerify="true"> - <action android:name="android.intent.action.VIEW" /> - <category android:name="android.intent.category.DEFAULT" /> - <category android:name="android.intent.category.BROWSABLE" /> - <data android:scheme="http" /> - <data android:scheme="https" /> - <data android:host="explicit.example.com" /> - </intent-filter> - </activity> - </application> -</manifest> diff --git a/tests/AutoVerify/app1/res/values/strings.xml b/tests/AutoVerify/app1/res/values/strings.xml deleted file mode 100644 index e234355041c6..000000000000 --- a/tests/AutoVerify/app1/res/values/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -Copyright 2020 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. ---> - -<resources> - <!-- app icon label, do not translate --> - <string name="app_name" translatable="false">AutoVerify Test</string> -</resources> diff --git a/tests/AutoVerify/app2/Android.bp b/tests/AutoVerify/app2/Android.bp deleted file mode 100644 index 1c6c97bdf350..000000000000 --- a/tests/AutoVerify/app2/Android.bp +++ /dev/null @@ -1,11 +0,0 @@ -android_app { - name: "AutoVerifyTest2", - srcs: ["src/**/*.java"], - resource_dirs: ["res"], - platform_apis: true, - min_sdk_version: "26", - target_sdk_version: "26", - optimize: { - enabled: false, - }, -} diff --git a/tests/AutoVerify/app2/AndroidManifest.xml b/tests/AutoVerify/app2/AndroidManifest.xml deleted file mode 100644 index a00807883cfc..000000000000 --- a/tests/AutoVerify/app2/AndroidManifest.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2020 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. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.test.autoverify" > - - <uses-sdk android:targetSdkVersion="26" /> - - <application - android:label="@string/app_name" > - <activity - android:name=".MainActivity" - android:label="@string/app_name" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - - <intent-filter android:autoVerify="true"> - <action android:name="android.intent.action.VIEW" /> - <category android:name="android.intent.category.DEFAULT" /> - <category android:name="android.intent.category.BROWSABLE" /> - <data android:scheme="http" /> - <data android:scheme="https" /> - <data android:host="explicit.example.com" /> - <data android:host="*.wildcard.tld" /> - </intent-filter> - </activity> - </application> -</manifest> diff --git a/tests/AutoVerify/app2/res/values/strings.xml b/tests/AutoVerify/app2/res/values/strings.xml deleted file mode 100644 index e234355041c6..000000000000 --- a/tests/AutoVerify/app2/res/values/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -Copyright 2020 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. ---> - -<resources> - <!-- app icon label, do not translate --> - <string name="app_name" translatable="false">AutoVerify Test</string> -</resources> diff --git a/tests/AutoVerify/app3/Android.bp b/tests/AutoVerify/app3/Android.bp deleted file mode 100644 index 70a2b77d1000..000000000000 --- a/tests/AutoVerify/app3/Android.bp +++ /dev/null @@ -1,11 +0,0 @@ -android_app { - name: "AutoVerifyTest3", - srcs: ["src/**/*.java"], - resource_dirs: ["res"], - platform_apis: true, - min_sdk_version: "26", - target_sdk_version: "26", - optimize: { - enabled: false, - }, -} diff --git a/tests/AutoVerify/app3/AndroidManifest.xml b/tests/AutoVerify/app3/AndroidManifest.xml deleted file mode 100644 index efaabc9a38d3..000000000000 --- a/tests/AutoVerify/app3/AndroidManifest.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2020 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. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.test.autoverify" > - - <uses-sdk android:targetSdkVersion="26" /> - - <application - android:label="@string/app_name" > - <activity - android:name=".MainActivity" - android:label="@string/app_name" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - - <!-- does not request autoVerify --> - <intent-filter> - <action android:name="android.intent.action.VIEW" /> - <category android:name="android.intent.category.DEFAULT" /> - <category android:name="android.intent.category.BROWSABLE" /> - <data android:scheme="http" /> - <data android:scheme="https" /> - <data android:host="explicit.example.com" /> - </intent-filter> - </activity> - </application> -</manifest> diff --git a/tests/AutoVerify/app3/res/values/strings.xml b/tests/AutoVerify/app3/res/values/strings.xml deleted file mode 100644 index e234355041c6..000000000000 --- a/tests/AutoVerify/app3/res/values/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -Copyright 2020 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. ---> - -<resources> - <!-- app icon label, do not translate --> - <string name="app_name" translatable="false">AutoVerify Test</string> -</resources> diff --git a/tests/AutoVerify/app4/Android.bp b/tests/AutoVerify/app4/Android.bp deleted file mode 100644 index fbdae1181a7a..000000000000 --- a/tests/AutoVerify/app4/Android.bp +++ /dev/null @@ -1,11 +0,0 @@ -android_app { - name: "AutoVerifyTest4", - srcs: ["src/**/*.java"], - resource_dirs: ["res"], - platform_apis: true, - min_sdk_version: "26", - target_sdk_version: "26", - optimize: { - enabled: false, - }, -} diff --git a/tests/AutoVerify/app4/AndroidManifest.xml b/tests/AutoVerify/app4/AndroidManifest.xml deleted file mode 100644 index 1c975f8336c9..000000000000 --- a/tests/AutoVerify/app4/AndroidManifest.xml +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2020 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. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.test.autoverify" > - - <uses-sdk android:targetSdkVersion="26" /> - - <application - android:label="@string/app_name" > - <activity - android:name=".MainActivity" - android:label="@string/app_name" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - - <!-- intentionally does not autoVerify --> - <intent-filter> - <action android:name="android.intent.action.VIEW" /> - <category android:name="android.intent.category.DEFAULT" /> - <category android:name="android.intent.category.BROWSABLE" /> - <data android:scheme="http" /> - <data android:scheme="https" /> - <data android:host="explicit.example.com" /> - <data android:host="*.wildcard.tld" /> - </intent-filter> - </activity> - </application> -</manifest> diff --git a/tests/AutoVerify/app4/res/values/strings.xml b/tests/AutoVerify/app4/res/values/strings.xml deleted file mode 100644 index e234355041c6..000000000000 --- a/tests/AutoVerify/app4/res/values/strings.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -Copyright 2020 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. ---> - -<resources> - <!-- app icon label, do not translate --> - <string name="app_name" translatable="false">AutoVerify Test</string> -</resources> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt index 9a8e37b19e56..57d6127b1cd1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt @@ -88,7 +88,7 @@ class OpenAppWarmTest( noUncoveredRegions(Surface.ROTATION_0, rotation, bugId = 141361128) navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation) statusBarLayerRotatesScales(Surface.ROTATION_0, rotation) - navBarLayerIsAlwaysVisible() + navBarLayerIsAlwaysVisible(bugId = 140855415) statusBarLayerIsAlwaysVisible(enabled = false) wallpaperLayerBecomesInvisible() } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt index 91ec211805f7..279092d716e2 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt @@ -87,9 +87,9 @@ class OpenAppToSplitScreenTest( } layersTrace { - navBarLayerIsAlwaysVisible() + navBarLayerIsAlwaysVisible(bugId = 140855415) statusBarLayerIsAlwaysVisible() - noUncoveredRegions(rotation) + noUncoveredRegions(rotation, enabled = false) navBarLayerRotatesAndScales(rotation, bugId = 140855415) statusBarLayerRotatesScales(rotation) diff --git a/tests/RollbackTest/README.txt b/tests/RollbackTest/README.txt index c0b718a3e2c1..bc3b3bc3a1ee 100644 --- a/tests/RollbackTest/README.txt +++ b/tests/RollbackTest/README.txt @@ -9,10 +9,10 @@ StagedRollbackTest - device driven test for staged rollbacks. TestApp - - source for dummy apks used in testing. + - source for fake apks used in testing. TestApex - - source for dummy apex modules used in testing. + - source for fake apex modules used in testing. Running the tests ================= diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp index 530d0e492f2e..76f8df02465b 100644 --- a/tests/StagedInstallTest/Android.bp +++ b/tests/StagedInstallTest/Android.bp @@ -18,6 +18,9 @@ android_test_helper_app { srcs: ["app/src/**/*.java"], static_libs: ["androidx.test.rules", "cts-install-lib"], test_suites: ["general-tests"], + java_resources: [ + ":com.android.apex.apkrollback.test_v2", + ], } java_test_host { diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java index 02597d548361..781723985ec5 100644 --- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java @@ -49,6 +49,11 @@ import java.util.function.Consumer; public class StagedInstallInternalTest { private static final String TAG = StagedInstallInternalTest.class.getSimpleName(); + private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test"; + private static final TestApp TEST_APEX_WITH_APK_V1 = new TestApp("TestApexWithApkV1", + APK_IN_APEX_TESTAPEX_NAME, 1, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v1.apex"); + private static final TestApp TEST_APEX_WITH_APK_V2 = new TestApp("TestApexWithApkV2", + APK_IN_APEX_TESTAPEX_NAME, 2, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v2.apex"); private File mTestStateFile = new File( InstrumentationRegistry.getInstrumentation().getContext().getFilesDir(), @@ -82,6 +87,24 @@ public class StagedInstallInternalTest { } @Test + public void testDuplicateApkInApexShouldFail_Commit() throws Exception { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); + // Duplicate packages(TestApp.A) in TEST_APEX_WITH_APK_V2(apk-in-apex) and TestApp.A2(apk) + // should fail to install. + int sessionId = Install.multi(TEST_APEX_WITH_APK_V2, TestApp.A2).setStaged().commit(); + storeSessionId(sessionId); + } + + @Test + public void testDuplicateApkInApexShouldFail_Verify() throws Exception { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); + int sessionId = retrieveLastSessionId(); + PackageInstaller.SessionInfo info = + InstallUtils.getPackageInstaller().getSessionInfo(sessionId); + assertThat(info.isStagedSessionFailed()).isTrue(); + } + + @Test public void testSystemServerRestartDoesNotAffectStagedSessions_Commit() throws Exception { int sessionId = Install.single(TestApp.A1).setStaged().commit(); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); @@ -96,6 +119,18 @@ public class StagedInstallInternalTest { assertSessionReady(sessionId); } + @Test + public void testAbandonStagedSessionShouldCleanUp() throws Exception { + int id1 = Install.single(TestApp.A1).setStaged().createSession(); + InstallUtils.getPackageInstaller().abandonSession(id1); + int id2 = Install.multi(TestApp.A1).setStaged().createSession(); + InstallUtils.getPackageInstaller().abandonSession(id2); + int id3 = Install.single(TestApp.A1).setStaged().commit(); + InstallUtils.getPackageInstaller().abandonSession(id3); + int id4 = Install.multi(TestApp.A1).setStaged().commit(); + InstallUtils.getPackageInstaller().abandonSession(id4); + } + private static void assertSessionReady(int sessionId) { assertSessionState(sessionId, (session) -> assertThat(session.isStagedSessionReady()).isTrue()); diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java index 55def498a0cd..d7b07967f979 100644 --- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java @@ -24,11 +24,14 @@ import static org.junit.Assume.assumeTrue; import android.cts.install.lib.host.InstallUtilsHost; +import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; import com.android.ddmlib.Log; import com.android.tests.rollback.host.AbandonSessionsRule; import com.android.tests.util.ModuleTestUtils; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.tradefed.util.CommandResult; +import com.android.tradefed.util.CommandStatus; import com.android.tradefed.util.ProcessInfo; import org.junit.After; @@ -38,6 +41,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; @RunWith(DeviceJUnit4ClassRunner.class) public class StagedInstallInternalTest extends BaseHostJUnit4Test { @@ -49,6 +55,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this); private static final String SHIM_V2 = "com.android.apex.cts.shim.v2.apex"; private static final String APK_A = "TestAppAv1.apk"; + private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test"; private final ModuleTestUtils mTestUtils = new ModuleTestUtils(this); private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); @@ -74,6 +81,8 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { } catch (AssertionError e) { Log.e(TAG, e); } + deleteFiles("/system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex", + "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex"); } @Before @@ -86,6 +95,55 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { cleanUp(); } + /** + * Deletes files and reboots the device if necessary. + * @param files the paths of files which might contain wildcards + */ + private void deleteFiles(String... files) throws Exception { + boolean found = false; + for (String file : files) { + CommandResult result = getDevice().executeShellV2Command("ls " + file); + if (result.getStatus() == CommandStatus.SUCCESS) { + found = true; + break; + } + } + + if (found) { + if (!getDevice().isAdbRoot()) { + getDevice().enableAdbRoot(); + } + getDevice().remountSystemWritable(); + for (String file : files) { + getDevice().executeShellCommand("rm -rf " + file); + } + getDevice().reboot(); + } + } + + private void pushTestApex() throws Exception { + CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild()); + final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex"; + final File apex = buildHelper.getTestFile(fileName); + if (!getDevice().isAdbRoot()) { + getDevice().enableAdbRoot(); + } + getDevice().remountSystemWritable(); + assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName)); + getDevice().reboot(); + } + + /** + * Tests that duplicate packages in apk-in-apex and apk should fail to install. + */ + @Test + public void testDuplicateApkInApexShouldFail() throws Exception { + pushTestApex(); + runPhase("testDuplicateApkInApexShouldFail_Commit"); + getDevice().reboot(); + runPhase("testDuplicateApkInApexShouldFail_Verify"); + } + @Test public void testSystemServerRestartDoesNotAffectStagedSessions() throws Exception { runPhase("testSystemServerRestartDoesNotAffectStagedSessions_Commit"); @@ -145,6 +203,28 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { assertThat(sessionIds.length).isEqualTo(3); } + @Test + public void testAbandonStagedSessionShouldCleanUp() throws Exception { + List<String> before = getStagingDirectories(); + runPhase("testAbandonStagedSessionShouldCleanUp"); + List<String> after = getStagingDirectories(); + // The staging directories generated during the test should be deleted + assertThat(after).isEqualTo(before); + } + + private List<String> getStagingDirectories() { + String baseDir = "/data/app-staging"; + try { + return getDevice().getFileEntry(baseDir).getChildren(false) + .stream().filter(entry -> entry.getName().matches("session_\\d+")) + .map(entry -> entry.getName()) + .collect(Collectors.toList()); + } catch (Exception e) { + // Return an empty list if any error + return Collections.EMPTY_LIST; + } + } + private void restartSystemServer() throws Exception { // Restart the system server ProcessInfo oldPs = getDevice().getProcessByName("system_server"); diff --git a/tests/net/common/java/android/net/DhcpInfoTest.java b/tests/net/common/java/android/net/DhcpInfoTest.java index 4d45ad72a9b8..ab4726bab573 100644 --- a/tests/net/common/java/android/net/DhcpInfoTest.java +++ b/tests/net/common/java/android/net/DhcpInfoTest.java @@ -17,8 +17,8 @@ package android.net; import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTL; -import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals; -import static com.android.testutils.ParcelUtilsKt.parcelingRoundTrip; +import static com.android.testutils.MiscAsserts.assertFieldCountEquals; +import static com.android.testutils.ParcelUtils.parcelingRoundTrip; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/tests/net/common/java/android/net/IpPrefixTest.java b/tests/net/common/java/android/net/IpPrefixTest.java index 985e10df3961..9c0fc7ce7881 100644 --- a/tests/net/common/java/android/net/IpPrefixTest.java +++ b/tests/net/common/java/android/net/IpPrefixTest.java @@ -16,10 +16,10 @@ package android.net; -import static com.android.testutils.MiscAssertsKt.assertEqualBothWays; -import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals; -import static com.android.testutils.MiscAssertsKt.assertNotEqualEitherWay; -import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; +import static com.android.testutils.MiscAsserts.assertEqualBothWays; +import static com.android.testutils.MiscAsserts.assertFieldCountEquals; +import static com.android.testutils.MiscAsserts.assertNotEqualEitherWay; +import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java index c74c112490f8..60308e32b88d 100644 --- a/tests/net/common/java/android/net/LinkAddressTest.java +++ b/tests/net/common/java/android/net/LinkAddressTest.java @@ -27,10 +27,10 @@ import static android.system.OsConstants.RT_SCOPE_LINK; import static android.system.OsConstants.RT_SCOPE_SITE; import static android.system.OsConstants.RT_SCOPE_UNIVERSE; -import static com.android.testutils.MiscAssertsKt.assertEqualBothWays; -import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals; -import static com.android.testutils.MiscAssertsKt.assertNotEqualEitherWay; -import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; +import static com.android.testutils.MiscAsserts.assertEqualBothWays; +import static com.android.testutils.MiscAsserts.assertFieldCountEquals; +import static com.android.testutils.MiscAsserts.assertNotEqualEitherWay; +import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java index 6eba62e63740..3c3076f11727 100644 --- a/tests/net/common/java/android/net/LinkPropertiesTest.java +++ b/tests/net/common/java/android/net/LinkPropertiesTest.java @@ -20,9 +20,9 @@ import static android.net.RouteInfo.RTN_THROW; import static android.net.RouteInfo.RTN_UNICAST; import static android.net.RouteInfo.RTN_UNREACHABLE; -import static com.android.testutils.ParcelUtilsKt.assertParcelSane; -import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; -import static com.android.testutils.ParcelUtilsKt.parcelingRoundTrip; +import static com.android.testutils.ParcelUtils.assertParcelSane; +import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; +import static com.android.testutils.ParcelUtils.parcelingRoundTrip; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java index 3f8261d5ad7f..e1693129892f 100644 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java @@ -42,8 +42,8 @@ import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES; -import static com.android.testutils.ParcelUtilsKt.assertParcelSane; -import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; +import static com.android.testutils.ParcelUtils.assertParcelSane; +import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; diff --git a/tests/net/common/java/android/net/RouteInfoTest.java b/tests/net/common/java/android/net/RouteInfoTest.java index 60cac0b6b0f5..71689f919726 100644 --- a/tests/net/common/java/android/net/RouteInfoTest.java +++ b/tests/net/common/java/android/net/RouteInfoTest.java @@ -18,10 +18,10 @@ package android.net; import static android.net.RouteInfo.RTN_UNREACHABLE; -import static com.android.testutils.MiscAssertsKt.assertEqualBothWays; -import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals; -import static com.android.testutils.MiscAssertsKt.assertNotEqualEitherWay; -import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; +import static com.android.testutils.MiscAsserts.assertEqualBothWays; +import static com.android.testutils.MiscAsserts.assertFieldCountEquals; +import static com.android.testutils.MiscAsserts.assertNotEqualEitherWay; +import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java index 84805442e5c7..d50406fd3a1c 100644 --- a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java +++ b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java @@ -16,7 +16,7 @@ package android.net.apf; -import static com.android.testutils.ParcelUtilsKt.assertParcelSane; +import static com.android.testutils.ParcelUtils.assertParcelSane; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt b/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt index fa2b99ce5cc6..165fd3728281 100644 --- a/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt +++ b/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt @@ -14,6 +14,8 @@ * limitations under the License */ +@file:JvmName("ConnectivityServiceTestUtils") + package com.android.server import android.net.ConnectivityManager.TYPE_BLUETOOTH diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java index 0ffafd45613a..9f0b41fa0cdf 100644 --- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java +++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java @@ -24,7 +24,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; -import static com.android.server.ConnectivityServiceTestUtilsKt.transportToLegacyType; +import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType; import static junit.framework.Assert.assertTrue; @@ -49,7 +49,7 @@ import android.os.Message; import android.util.Log; import com.android.server.connectivity.ConnectivityConstants; -import com.android.testutils.HandlerUtilsKt; +import com.android.testutils.HandlerUtils; import com.android.testutils.TestableNetworkCallback; import java.util.Set; @@ -265,6 +265,6 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { } public void waitForIdle(long timeoutMs) { - HandlerUtilsKt.waitForIdle(mHandlerThread, timeoutMs); + HandlerUtils.waitForIdle(mHandlerThread, timeoutMs); } } diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java index 1d6c10766792..06e9405a6a79 100644 --- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java +++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java @@ -21,7 +21,7 @@ import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnostics import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport; import static android.net.ConnectivityDiagnosticsManager.DataStallReport; -import static com.android.testutils.ParcelUtilsKt.assertParcelSane; +import static com.android.testutils.ParcelUtils.assertParcelSane; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java index c9888b24b6da..25e225ef303a 100644 --- a/tests/net/java/android/net/IpSecConfigTest.java +++ b/tests/net/java/android/net/IpSecConfigTest.java @@ -16,8 +16,8 @@ package android.net; -import static com.android.testutils.ParcelUtilsKt.assertParcelSane; -import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; +import static com.android.testutils.ParcelUtils.assertParcelSane; +import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; diff --git a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java index cea8c5713a6b..835a83e9ddc7 100644 --- a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java +++ b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java @@ -16,7 +16,7 @@ package android.net; -import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; +import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; diff --git a/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java b/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java index efb92033df1e..6714bb1abbe6 100644 --- a/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java +++ b/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java @@ -16,7 +16,7 @@ package android.net; -import static com.android.testutils.ParcelUtilsKt.assertParcelSane; +import static com.android.testutils.ParcelUtils.assertParcelSane; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/tests/net/java/android/net/nsd/NsdManagerTest.java b/tests/net/java/android/net/nsd/NsdManagerTest.java index cf7587a2039f..b0a9b8a55322 100644 --- a/tests/net/java/android/net/nsd/NsdManagerTest.java +++ b/tests/net/java/android/net/nsd/NsdManagerTest.java @@ -38,7 +38,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.AsyncChannel; -import com.android.testutils.HandlerUtilsKt; +import com.android.testutils.HandlerUtils; import org.junit.After; import org.junit.Before; @@ -73,7 +73,7 @@ public class NsdManagerTest { @After public void tearDown() throws Exception { - HandlerUtilsKt.waitForIdle(mServiceHandler, mTimeoutMs); + HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs); mServiceHandler.chan.disconnect(); mServiceHandler.stop(); if (mManager != null) { @@ -333,7 +333,7 @@ public class NsdManagerTest { } int verifyRequest(int expectedMessageType) { - HandlerUtilsKt.waitForIdle(mServiceHandler, mTimeoutMs); + HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs); verify(mServiceHandler, timeout(mTimeoutMs)).handleMessage(any()); reset(mServiceHandler); Message received = mServiceHandler.getLastMessage(); diff --git a/tests/net/java/com/android/internal/net/VpnProfileTest.java b/tests/net/java/com/android/internal/net/VpnProfileTest.java index e5daa71c30ea..46597d19ef1b 100644 --- a/tests/net/java/com/android/internal/net/VpnProfileTest.java +++ b/tests/net/java/com/android/internal/net/VpnProfileTest.java @@ -16,7 +16,7 @@ package com.android.internal.net; -import static com.android.testutils.ParcelUtilsKt.assertParcelSane; +import static com.android.testutils.ParcelUtils.assertParcelSane; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 0f24b0c1c79b..a3673df1c713 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -78,16 +78,16 @@ import static android.net.RouteInfo.RTN_UNREACHABLE; import static android.os.Process.INVALID_UID; import static android.system.OsConstants.IPPROTO_TCP; -import static com.android.server.ConnectivityServiceTestUtilsKt.transportToLegacyType; -import static com.android.testutils.ConcurrentUtilsKt.await; -import static com.android.testutils.ConcurrentUtilsKt.durationOf; +import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType; +import static com.android.testutils.ConcurrentUtils.await; +import static com.android.testutils.ConcurrentUtils.durationOf; import static com.android.testutils.ExceptionUtils.ignoreExceptions; -import static com.android.testutils.HandlerUtilsKt.waitForIdleSerialExecutor; -import static com.android.testutils.MiscAssertsKt.assertContainsExactly; -import static com.android.testutils.MiscAssertsKt.assertEmpty; -import static com.android.testutils.MiscAssertsKt.assertLength; -import static com.android.testutils.MiscAssertsKt.assertRunsInAtMost; -import static com.android.testutils.MiscAssertsKt.assertThrows; +import static com.android.testutils.HandlerUtils.waitForIdleSerialExecutor; +import static com.android.testutils.MiscAsserts.assertContainsExactly; +import static com.android.testutils.MiscAsserts.assertEmpty; +import static com.android.testutils.MiscAsserts.assertLength; +import static com.android.testutils.MiscAsserts.assertRunsInAtMost; +import static com.android.testutils.MiscAsserts.assertThrows; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -242,7 +242,7 @@ import com.android.server.connectivity.Vpn; import com.android.server.net.NetworkPinner; import com.android.server.net.NetworkPolicyManagerInternal; import com.android.testutils.ExceptionUtils; -import com.android.testutils.HandlerUtilsKt; +import com.android.testutils.HandlerUtils; import com.android.testutils.RecorderCallback.CallbackEntry; import com.android.testutils.TestableNetworkCallback; @@ -518,12 +518,12 @@ public class ConnectivityServiceTest { } private void waitForIdle() { - HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); waitForIdle(mCellNetworkAgent, TIMEOUT_MS); waitForIdle(mWiFiNetworkAgent, TIMEOUT_MS); waitForIdle(mEthernetNetworkAgent, TIMEOUT_MS); - HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); - HandlerUtilsKt.waitForIdle(ConnectivityThread.get(), TIMEOUT_MS); + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + HandlerUtils.waitForIdle(ConnectivityThread.get(), TIMEOUT_MS); } private void waitForIdle(TestNetworkAgentWrapper agent, long timeoutMs) { @@ -614,8 +614,8 @@ public class ConnectivityServiceTest { // Waits for the NetworkAgent to be registered, which includes the creation of the // NetworkMonitor. waitForIdle(TIMEOUT_MS); - HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); - HandlerUtilsKt.waitForIdle(ConnectivityThread.get(), TIMEOUT_MS); + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + HandlerUtils.waitForIdle(ConnectivityThread.get(), TIMEOUT_MS); } @Override @@ -7099,7 +7099,7 @@ public class ConnectivityServiceTest { mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName()); // Block until all other events are done processing. - HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt()); verify(mConnectivityDiagnosticsCallback).asBinder(); @@ -7122,7 +7122,7 @@ public class ConnectivityServiceTest { mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName()); // Block until all other events are done processing. - HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt()); verify(mConnectivityDiagnosticsCallback).asBinder(); @@ -7133,7 +7133,7 @@ public class ConnectivityServiceTest { mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName()); // Block until all other events are done processing. - HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder)); } @@ -7285,7 +7285,7 @@ public class ConnectivityServiceTest { mConnectivityDiagnosticsCallback, request, mContext.getPackageName()); // Block until all other events are done processing. - HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); verify(mConnectivityDiagnosticsCallback) .onConnectivityReportAvailable(argThat(report -> { @@ -7305,7 +7305,7 @@ public class ConnectivityServiceTest { mConnectivityDiagnosticsCallback, request, mContext.getPackageName()); // Block until all other events are done processing. - HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); // Connect the cell agent verify that it notifies TestNetworkCallback that it is available final TestNetworkCallback callback = new TestNetworkCallback(); @@ -7322,7 +7322,7 @@ public class ConnectivityServiceTest { setUpConnectivityDiagnosticsCallback(); // Block until all other events are done processing. - HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); // Verify onConnectivityReport fired verify(mConnectivityDiagnosticsCallback).onConnectivityReportAvailable( @@ -7343,7 +7343,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.notifyDataStallSuspected(); // Block until all other events are done processing. - HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); // Verify onDataStallSuspected fired verify(mConnectivityDiagnosticsCallback).onDataStallSuspected( @@ -7364,7 +7364,7 @@ public class ConnectivityServiceTest { mService.reportNetworkConnectivity(n, hasConnectivity); // Block until all other events are done processing. - HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); // Verify onNetworkConnectivityReported fired verify(mConnectivityDiagnosticsCallback) @@ -7374,7 +7374,7 @@ public class ConnectivityServiceTest { mService.reportNetworkConnectivity(n, noConnectivity); // Block until all other events are done processing. - HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); // Wait for onNetworkConnectivityReported to fire verify(mConnectivityDiagnosticsCallback) diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java index 508b5cd9cb19..753dbf80b449 100644 --- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java @@ -26,9 +26,9 @@ import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE; import static android.provider.Settings.Global.PRIVATE_DNS_MODE; import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER; -import static com.android.testutils.MiscAssertsKt.assertContainsExactly; -import static com.android.testutils.MiscAssertsKt.assertContainsStringsExactly; -import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals; +import static com.android.testutils.MiscAsserts.assertContainsExactly; +import static com.android.testutils.MiscAsserts.assertContainsStringsExactly; +import static com.android.testutils.MiscAsserts.assertFieldCountEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java index aef9386755d7..8ccea1aa3474 100644 --- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java +++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java @@ -19,7 +19,7 @@ package com.android.server.connectivity; import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO; import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME; -import static com.android.testutils.MiscAssertsKt.assertStringContains; +import static com.android.testutils.MiscAsserts.assertStringContains; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java index a384687e06f6..ab12ac0ef56e 100644 --- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -28,11 +28,17 @@ import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRODUCT; import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_VENDOR; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.MATCH_ANY_USER; +import static android.net.INetd.PERMISSION_INTERNET; +import static android.net.INetd.PERMISSION_NONE; +import static android.net.INetd.PERMISSION_SYSTEM; +import static android.net.INetd.PERMISSION_UNINSTALLED; +import static android.net.INetd.PERMISSION_UPDATE_DEVICE_STATS; import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.os.Process.SYSTEM_UID; import static com.android.server.connectivity.PermissionMonitor.NETWORK; import static com.android.server.connectivity.PermissionMonitor.SYSTEM; +import static com.android.server.connectivity.PermissionMonitor.UidNetdPermissionInfo; import static junit.framework.Assert.fail; @@ -63,7 +69,7 @@ import android.net.UidRange; import android.os.Build; import android.os.UserHandle; import android.os.UserManager; -import android.util.SparseIntArray; +import android.util.SparseArray; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -312,7 +318,7 @@ public class PermissionMonitorTest { // Add hook to verify and track result of setPermission. doAnswer((InvocationOnMock invocation) -> { final Object[] args = invocation.getArguments(); - final Boolean isSystem = args[0].equals(INetd.PERMISSION_SYSTEM); + final Boolean isSystem = args[0].equals(PERMISSION_SYSTEM); for (final int uid : (int[]) args[1]) { // TODO: Currently, permission monitor will send duplicate commands for each uid // corresponding to each user. Need to fix that and uncomment below test. @@ -555,39 +561,40 @@ public class PermissionMonitorTest { // SYSTEM_UID1: SYSTEM_PACKAGE1 has internet permission and update device stats permission. // SYSTEM_UID2: SYSTEM_PACKAGE2 has only update device stats permission. - SparseIntArray netdPermissionsAppIds = new SparseIntArray(); - netdPermissionsAppIds.put(MOCK_UID1, INetd.PERMISSION_INTERNET); - netdPermissionsAppIds.put(MOCK_UID2, INetd.PERMISSION_NONE); - netdPermissionsAppIds.put(SYSTEM_UID1, INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS); - netdPermissionsAppIds.put(SYSTEM_UID2, INetd.PERMISSION_UPDATE_DEVICE_STATS); + final SparseArray<UidNetdPermissionInfo> uidsPermInfo = new SparseArray<>(); + uidsPermInfo.put(MOCK_UID1, new UidNetdPermissionInfo(PERMISSION_INTERNET)); + uidsPermInfo.put(MOCK_UID2, new UidNetdPermissionInfo(PERMISSION_NONE)); + uidsPermInfo.put(SYSTEM_UID1, new UidNetdPermissionInfo( + PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS)); + uidsPermInfo.put(SYSTEM_UID2, new UidNetdPermissionInfo(PERMISSION_UPDATE_DEVICE_STATS)); // Send the permission information to netd, expect permission updated. - mPermissionMonitor.sendPackagePermissionsToNetd(netdPermissionsAppIds); + mPermissionMonitor.sendPackagePermissionsToNetd(uidsPermInfo); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, + mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET, new int[]{MOCK_UID1}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{MOCK_UID2}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{SYSTEM_UID1}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UPDATE_DEVICE_STATS, + mNetdServiceMonitor.expectPermission(PERMISSION_NONE, new int[]{MOCK_UID2}); + mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET + | PERMISSION_UPDATE_DEVICE_STATS, new int[]{SYSTEM_UID1}); + mNetdServiceMonitor.expectPermission(PERMISSION_UPDATE_DEVICE_STATS, new int[]{SYSTEM_UID2}); // Update permission of MOCK_UID1, expect new permission show up. - mPermissionMonitor.sendPackagePermissionsForUid(MOCK_UID1, - INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); + mPermissionMonitor.sendPackagePermissionsForUid(MOCK_UID1, new UidNetdPermissionInfo( + PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS)); + mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET + | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); // Change permissions of SYSTEM_UID2, expect new permission show up and old permission // revoked. - mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID2, - INetd.PERMISSION_INTERNET); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{SYSTEM_UID2}); + mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID2, new UidNetdPermissionInfo( + PERMISSION_INTERNET)); + mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET, new int[]{SYSTEM_UID2}); // Revoke permission from SYSTEM_UID1, expect no permission stored. - mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID1, INetd.PERMISSION_NONE); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{SYSTEM_UID1}); + mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID1, new UidNetdPermissionInfo( + PERMISSION_NONE)); + mNetdServiceMonitor.expectPermission(PERMISSION_NONE, new int[]{SYSTEM_UID1}); } private PackageInfo setPackagePermissions(String packageName, int uid, String[] permissions) @@ -611,11 +618,11 @@ public class PermissionMonitorTest { final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); + mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET + | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); addPackage(MOCK_PACKAGE2, MOCK_UID2, new String[] {INTERNET}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID2}); + mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET, new int[]{MOCK_UID2}); } @Test @@ -623,8 +630,8 @@ public class PermissionMonitorTest { final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); + mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET + | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); // Install another package with the same uid and no permissions should not cause the UID to // lose permissions. @@ -633,8 +640,8 @@ public class PermissionMonitorTest { when(mPackageManager.getPackagesForUid(MOCK_UID1)) .thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2}); mPermissionMonitor.onPackageAdded(MOCK_PACKAGE2, MOCK_UID1); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); + mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET + | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); } @Test @@ -642,12 +649,12 @@ public class PermissionMonitorTest { final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); + mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET + | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{}); mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1}); + mNetdServiceMonitor.expectPermission(PERMISSION_UNINSTALLED, new int[]{MOCK_UID1}); } @Test @@ -655,16 +662,16 @@ public class PermissionMonitorTest { final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); + mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET + | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{}); removeAllPermissions(MOCK_UID1); mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1}); + mNetdServiceMonitor.expectPermission(PERMISSION_UNINSTALLED, new int[]{MOCK_UID1}); addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1}); + mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET, new int[]{MOCK_UID1}); } @Test @@ -672,10 +679,10 @@ public class PermissionMonitorTest { final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{MOCK_UID1}); + mNetdServiceMonitor.expectPermission(PERMISSION_NONE, new int[]{MOCK_UID1}); addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1}); + mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET, new int[]{MOCK_UID1}); } @Test @@ -683,8 +690,8 @@ public class PermissionMonitorTest { final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); + mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET + | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); // Mock another package with the same uid but different permissions. final PackageInfo packageInfo2 = buildPackageInfo(PARTITION_SYSTEM, MOCK_UID1, MOCK_USER1); @@ -695,7 +702,7 @@ public class PermissionMonitorTest { addPermissions(MOCK_UID1, INTERNET); mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1}); + mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET, new int[]{MOCK_UID1}); } @Test diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index de1c5759ee87..e8c4ee9c628d 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -270,12 +270,12 @@ public class VpnTest { } @Test - public void testUidWhiteAndBlacklist() throws Exception { + public void testUidAllowAndDenylist() throws Exception { final Vpn vpn = createVpn(primaryUser.id); final UidRange user = UidRange.createForUser(primaryUser.id); final String[] packages = {PKGS[0], PKGS[1], PKGS[2]}; - // Whitelist + // Allowed list final Set<UidRange> allow = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, Arrays.asList(packages), null); assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] { @@ -283,7 +283,7 @@ public class VpnTest { new UidRange(user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]) })), allow); - // Blacklist + // Denied list final Set<UidRange> disallow = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, Arrays.asList(packages)); assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] { @@ -354,11 +354,11 @@ public class VpnTest { } @Test - public void testLockdownWhitelist() throws Exception { + public void testLockdownAllowlist() throws Exception { final Vpn vpn = createVpn(primaryUser.id); final UidRange user = UidRange.createForUser(primaryUser.id); - // Set always-on with lockdown and whitelist app PKGS[2] from lockdown. + // Set always-on with lockdown and allow app PKGS[2] from lockdown. assertTrue(vpn.setAlwaysOnPackage( PKGS[1], true, Collections.singletonList(PKGS[2]), mKeyStore)); verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] { @@ -368,7 +368,7 @@ public class VpnTest { assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]); assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]); - // Change whitelisted app to PKGS[3]. + // Change allowed app list to PKGS[3]. assertTrue(vpn.setAlwaysOnPackage( PKGS[1], true, Collections.singletonList(PKGS[3]), mKeyStore)); verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] { @@ -395,7 +395,7 @@ public class VpnTest { assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]); assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]); - // Remove the whitelist. + // Remove the list of allowed packages. assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null, mKeyStore)); verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] { new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1), @@ -408,7 +408,7 @@ public class VpnTest { user.start + PKG_UIDS[3]); assertUnblocked(vpn, user.start + PKG_UIDS[0]); - // Add the whitelist. + // Add the list of allowed packages. assertTrue(vpn.setAlwaysOnPackage( PKGS[0], true, Collections.singletonList(PKGS[1]), mKeyStore)); verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] { @@ -421,12 +421,12 @@ public class VpnTest { assertBlocked(vpn, user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]); assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1]); - // Try whitelisting a package with a comma, should be rejected. + // Try allowing a package with a comma, should be rejected. assertFalse(vpn.setAlwaysOnPackage( PKGS[0], true, Collections.singletonList("a.b,c.d"), mKeyStore)); - // Pass a non-existent packages in the whitelist, they (and only they) should be ignored. - // Whitelisted package should change from PGKS[1] to PKGS[2]. + // Pass a non-existent packages in the allowlist, they (and only they) should be ignored. + // allowed package should change from PGKS[1] to PKGS[2]. assertTrue(vpn.setAlwaysOnPackage( PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app"), mKeyStore)); verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[]{ diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java index e83d2a90bffa..fb0cfc0d50ba 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java @@ -28,7 +28,7 @@ import static android.os.Process.myUid; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; -import static com.android.testutils.MiscAssertsKt.assertThrows; +import static com.android.testutils.MiscAsserts.assertThrows; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java index a6f7a36ff01b..291efc74aa47 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java @@ -53,7 +53,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.net.NetworkStatsServiceTest.LatchedHandler; -import com.android.testutils.HandlerUtilsKt; +import com.android.testutils.HandlerUtils; import org.junit.Before; import org.junit.Test; @@ -440,7 +440,7 @@ public class NetworkStatsObserversTest { } private void waitForObserverToIdle() { - HandlerUtilsKt.waitForIdle(mObserverHandlerThread, WAIT_TIMEOUT_MS); - HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT_MS); + HandlerUtils.waitForIdle(mObserverHandlerThread, WAIT_TIMEOUT_MS); + HandlerUtils.waitForIdle(mHandler, WAIT_TIMEOUT_MS); } } diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index 1307a849f1eb..7abe1893dd9e 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -109,7 +109,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.server.net.NetworkStatsService.NetworkStatsSettings; import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config; -import com.android.testutils.HandlerUtilsKt; +import com.android.testutils.HandlerUtils; import com.android.testutils.TestableNetworkStatsProviderBinder; import libcore.io.IoUtils; @@ -700,7 +700,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { when(mNetworkStatsSubscriptionsMonitor.getRatTypeForSubscriberId(anyString())) .thenReturn(ratType); mService.handleOnCollapsedRatTypeChanged(); - HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT); + HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); } @Test @@ -1065,7 +1065,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { long minThresholdInBytes = 2 * 1024 * 1024; // 2 MB assertEquals(minThresholdInBytes, request.thresholdInBytes); - HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT); + HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); // Make sure that the caller binder gets connected verify(mBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt()); @@ -1203,7 +1203,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { // Simulates alert quota of the provider has been reached. cb.notifyAlertReached(); - HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT); + HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); // Verifies that polling is triggered by alert reached. provider.expectOnRequestStatsUpdate(0 /* unused */); @@ -1264,7 +1264,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { // Call handleOnCollapsedRatTypeChanged manually to simulate the callback fired // when stopping monitor, this is needed by NetworkStatsService to trigger updateIfaces. mService.handleOnCollapsedRatTypeChanged(); - HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT); + HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); // Create some traffic. incrementCurrentTime(MINUTE_IN_MILLIS); // Append more traffic on existing snapshot. @@ -1286,7 +1286,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { setCombineSubtypeEnabled(false); mService.handleOnCollapsedRatTypeChanged(); - HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT); + HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); // Create some traffic. incrementCurrentTime(MINUTE_IN_MILLIS); // Append more traffic on existing snapshot. @@ -1520,7 +1520,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { } private void waitForIdle() { - HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT); + HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); } static class LatchedHandler extends Handler { diff --git a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java index c2a5459ae125..f80af034fa2b 100644 --- a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java +++ b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java @@ -62,12 +62,22 @@ public class SystemPreparer extends ExternalResource { private final RebootStrategy mRebootStrategy; private final TearDownRule mTearDownRule; + // When debugging, it may be useful to run a test case without rebooting the device afterwards, + // to manually verify the device state. + private boolean mDebugSkipAfterReboot; + public SystemPreparer(TemporaryFolder hostTempFolder, DeviceProvider deviceProvider) { this(hostTempFolder, RebootStrategy.FULL, null, deviceProvider); } public SystemPreparer(TemporaryFolder hostTempFolder, RebootStrategy rebootStrategy, @Nullable TestRuleDelegate testRuleDelegate, DeviceProvider deviceProvider) { + this(hostTempFolder, rebootStrategy, testRuleDelegate, false, deviceProvider); + } + + public SystemPreparer(TemporaryFolder hostTempFolder, RebootStrategy rebootStrategy, + @Nullable TestRuleDelegate testRuleDelegate, boolean debugSkipAfterReboot, + DeviceProvider deviceProvider) { mHostTempFolder = hostTempFolder; mDeviceProvider = deviceProvider; mRebootStrategy = rebootStrategy; @@ -75,6 +85,7 @@ public class SystemPreparer extends ExternalResource { if (testRuleDelegate != null) { testRuleDelegate.setDelegate(mTearDownRule); } + mDebugSkipAfterReboot = debugSkipAfterReboot; } /** Copies a file within the host test jar to a path on device. */ @@ -172,7 +183,9 @@ public class SystemPreparer extends ExternalResource { case USERSPACE_UNTIL_ONLINE: device.rebootUserspaceUntilOnline(); break; - case START_STOP: + // TODO(b/159540015): Make this START_STOP instead of default once it's fixed. Can't + // currently be done because START_STOP is commented out. + default: device.executeShellCommand("stop"); device.executeShellCommand("start"); ITestDevice.RecoveryMode cachedRecoveryMode = device.getRecoveryMode(); @@ -228,7 +241,9 @@ public class SystemPreparer extends ExternalResource { for (final String packageName : mInstalledPackages) { device.uninstallPackage(packageName); } - reboot(); + if (!mDebugSkipAfterReboot) { + reboot(); + } } catch (DeviceNotAvailableException e) { Assert.fail(e.toString()); } @@ -355,6 +370,7 @@ public class SystemPreparer extends ExternalResource { /** * How to reboot the device. Ordered from slowest to fastest. */ + @SuppressWarnings("DanglingJavadoc") public enum RebootStrategy { /** @see ITestDevice#reboot() */ FULL, @@ -374,7 +390,15 @@ public class SystemPreparer extends ExternalResource { * * TODO(b/159540015): There's a bug with this causing unnecessary disk space usage, which * can eventually lead to an insufficient storage space error. + * + * This can be uncommented for local development, but should be left out when merging. + * It is done this way to hopefully be caught by code review, since merging this will + * break all of postsubmit. But the nearly 50% reduction in test runtime is worth having + * this option exist. + * + * @deprecated do not use this in merged code until bug is resolved */ - START_STOP +// @Deprecated +// START_STOP } } diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py index 8a282e5f0821..da644021e30e 100755 --- a/tools/hiddenapi/generate_hiddenapi_lists.py +++ b/tools/hiddenapi/generate_hiddenapi_lists.py @@ -34,26 +34,6 @@ FLAG_PUBLIC_API = 'public-api' FLAG_SYSTEM_API = 'system-api' FLAG_TEST_API = 'test-api' -OLD_FLAG_SDK = "whitelist" -OLD_FLAG_UNSUPPORTED = "greylist" -OLD_FLAG_BLOCKED = "blacklist" -OLD_FLAG_MAX_TARGET_O = "greylist-max-o" -OLD_FLAG_MAX_TARGET_P = "greylist-max-p" -OLD_FLAG_MAX_TARGET_Q = "greylist-max-q" -OLD_FLAG_MAX_TARGET_R = "greylist-max-r" - -OLD_FLAGS_TO_NEW = { - OLD_FLAG_SDK: FLAG_SDK, - OLD_FLAG_UNSUPPORTED: FLAG_UNSUPPORTED, - OLD_FLAG_BLOCKED: FLAG_BLOCKED, - OLD_FLAG_MAX_TARGET_O: FLAG_MAX_TARGET_O, - OLD_FLAG_MAX_TARGET_P: FLAG_MAX_TARGET_P, - OLD_FLAG_MAX_TARGET_Q: FLAG_MAX_TARGET_Q, - OLD_FLAG_MAX_TARGET_R: FLAG_MAX_TARGET_R, -} - -NEW_FLAGS_TO_OLD = dict(zip(OLD_FLAGS_TO_NEW.values(), OLD_FLAGS_TO_NEW.keys())) - # List of all known flags. FLAGS_API_LIST = [ FLAG_SDK, @@ -205,36 +185,6 @@ class FlagsDict: "Please visit go/hiddenapi for more information.").format( source, "\n".join(flags_subset - ALL_FLAGS_SET)) - def convert_to_new_flag(self, flag): - """Converts old flag to a new variant. - - Flags that are considered old are replaced with new versions. - Otherwise, it is a no-op. - - Args: - flag: a string, representing SDK flag. - - Returns: - A string. Result of conversion. - - """ - return OLD_FLAGS_TO_NEW.get(flag, flag) - - def convert_to_old_flag(self, flag): - """Converts a new flag to a old variant. - - No-op if there is no suitable old flag. - Only used to support backwards compatibility. - - Args: - flag: a string, representing SDK flag. - - Returns: - A string. Result of conversion. - - """ - return NEW_FLAGS_TO_OLD.get(flag, flag) - def filter_apis(self, filter_fn): """Returns APIs which match a given predicate. @@ -272,7 +222,7 @@ class FlagsDict: """ lines = [] for api in self._dict: - flags = sorted([self.convert_to_old_flag(flag) for flag in self._dict[api]]) + flags = sorted(self._dict[api]) lines.append(",".join([api] + flags)) return sorted(lines) @@ -298,12 +248,12 @@ class FlagsDict: # Check that all flags are known. csv_flags = set() for csv in csv_values: - csv_flags.update([self.convert_to_new_flag(flag) for flag in csv[1:]]) + csv_flags.update(csv[1:]) self._check_flags_set(csv_flags, source) # Iterate over all CSV lines, find entry in dict and append flags to it. for csv in csv_values: - flags = [self.convert_to_new_flag(flag) for flag in csv[1:]] + flags = csv[1:] if (FLAG_PUBLIC_API in flags) or (FLAG_SYSTEM_API in flags): flags.append(FLAG_SDK) self._dict[csv[0]].update(flags) diff --git a/tools/hiddenapi/generate_hiddenapi_lists_test.py b/tools/hiddenapi/generate_hiddenapi_lists_test.py index 321c400ef898..82d117fbbbab 100755 --- a/tools/hiddenapi/generate_hiddenapi_lists_test.py +++ b/tools/hiddenapi/generate_hiddenapi_lists_test.py @@ -35,7 +35,7 @@ class TestHiddenapiListGeneration(unittest.TestCase): flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B', 'C']) flags.assign_flag(FLAG_UNSUPPORTED, set(['C'])) self.assertEqual(flags.generate_csv(), - [ 'A,' + OLD_FLAG_SDK, 'B', 'C,' + OLD_FLAG_UNSUPPORTED ]) + [ 'A,' + FLAG_SDK, 'B', 'C,' + FLAG_UNSUPPORTED ]) # Check three things: # (1) B is selected as valid unassigned @@ -50,8 +50,7 @@ class TestHiddenapiListGeneration(unittest.TestCase): # Test empty CSV entry. self.assertEqual(flags.generate_csv(), []) - # Test new additions. CSV generator produces values with old flags - # to be backwards compatible. + # Test new additions. flags.parse_and_merge_csv([ 'A,' + FLAG_UNSUPPORTED, 'B,' + FLAG_BLOCKED + ',' + FLAG_MAX_TARGET_O, @@ -60,11 +59,11 @@ class TestHiddenapiListGeneration(unittest.TestCase): 'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API, ]) self.assertEqual(flags.generate_csv(), [ - 'A,' + OLD_FLAG_UNSUPPORTED, - 'B,' + OLD_FLAG_BLOCKED + "," + OLD_FLAG_MAX_TARGET_O, - 'C,' + FLAG_SYSTEM_API + ',' + OLD_FLAG_SDK, - 'D,' + OLD_FLAG_UNSUPPORTED + ',' + FLAG_TEST_API, - 'E,' + OLD_FLAG_BLOCKED + ',' + FLAG_TEST_API, + 'A,' + FLAG_UNSUPPORTED, + 'B,' + FLAG_BLOCKED + "," + FLAG_MAX_TARGET_O, + 'C,' + FLAG_SYSTEM_API + ',' + FLAG_SDK, + 'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API, + 'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API, ]) # Test unknown flag. @@ -78,7 +77,7 @@ class TestHiddenapiListGeneration(unittest.TestCase): # Test new additions. flags.assign_flag(FLAG_UNSUPPORTED, set([ 'A', 'B' ])) self.assertEqual(flags.generate_csv(), - [ 'A,' + OLD_FLAG_UNSUPPORTED + "," + OLD_FLAG_SDK, 'B,' + OLD_FLAG_UNSUPPORTED ]) + [ 'A,' + FLAG_UNSUPPORTED + "," + FLAG_SDK, 'B,' + FLAG_UNSUPPORTED ]) # Test invalid API signature. with self.assertRaises(AssertionError): diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java index f919ea4c4797..393fe8d3ab9a 100644 --- a/wifi/java/android/net/wifi/SoftApConfiguration.java +++ b/wifi/java/android/net/wifi/SoftApConfiguration.java @@ -582,6 +582,7 @@ public final class SoftApConfiguration implements Parcelable { wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); break; case SECURITY_TYPE_WPA2_PSK: + case SECURITY_TYPE_WPA3_SAE_TRANSITION: wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA2_PSK); break; default: diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java index fa806e7797cd..282757ac5a14 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java +++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java @@ -448,6 +448,16 @@ public final class Credential implements Parcelable { return new UserCredential[size]; } }; + + /** + * Get a unique identifier for UserCredential. + * + * @hide + * @return a Unique identifier for a UserCredential object + */ + public int getUniqueId() { + return Objects.hash(mUsername); + } } private UserCredential mUserCredential = null; /** @@ -1037,7 +1047,8 @@ public final class Credential implements Parcelable { * @return a Unique identifier for a Credential object */ public int getUniqueId() { - return Objects.hash(mUserCredential, mCertCredential, mSimCredential, mRealm); + return Objects.hash(mUserCredential != null ? mUserCredential.getUniqueId() : 0, + mCertCredential, mSimCredential, mRealm); } @Override diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java index 224c4bed9d5b..8f34579f6a5d 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java +++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java @@ -313,9 +313,7 @@ public final class HomeSp implements Parcelable { * @return a Unique identifier for a HomeSp object */ public int getUniqueId() { - return Objects.hash(mFqdn, mFriendlyName, mHomeNetworkIds, Arrays.hashCode(mMatchAllOis), - Arrays.hashCode(mMatchAnyOis), Arrays.hashCode(mOtherHomePartners), - Arrays.hashCode(mRoamingConsortiumOis)); + return Objects.hash(mFqdn); } diff --git a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl index bfdd45d9f9b0..fd89d3b0486e 100644 --- a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl +++ b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl @@ -25,7 +25,7 @@ import android.os.Messenger; */ interface IWifiP2pManager { - Messenger getMessenger(in IBinder binder); + Messenger getMessenger(in IBinder binder, in String packageName); Messenger getP2pStateMachineMessenger(); oneway void close(in IBinder binder); void setMiracastMode(int mode); diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java index 724ccf0d7c45..ad38c5af07fc 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java @@ -1156,8 +1156,8 @@ public class WifiP2pManager { */ public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) { Binder binder = new Binder(); - Channel channel = initalizeChannel(srcContext, srcLooper, listener, getMessenger(binder), - binder); + Channel channel = initializeChannel(srcContext, srcLooper, listener, + getMessenger(binder, srcContext.getOpPackageName()), binder); return channel; } @@ -1167,12 +1167,12 @@ public class WifiP2pManager { */ public Channel initializeInternal(Context srcContext, Looper srcLooper, ChannelListener listener) { - return initalizeChannel(srcContext, srcLooper, listener, getP2pStateMachineMessenger(), + return initializeChannel(srcContext, srcLooper, listener, getP2pStateMachineMessenger(), null); } - private Channel initalizeChannel(Context srcContext, Looper srcLooper, ChannelListener listener, - Messenger messenger, Binder binder) { + private Channel initializeChannel(Context srcContext, Looper srcLooper, + ChannelListener listener, Messenger messenger, Binder binder) { if (messenger == null) return null; Channel c = new Channel(srcContext, srcLooper, listener, binder, this); @@ -1814,6 +1814,14 @@ public class WifiP2pManager { } } + private Messenger getMessenger(@NonNull Binder binder, @Nullable String packageName) { + try { + return mService.getMessenger(binder, packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Get a reference to WifiP2pService handler. This is used to establish * an AsyncChannel communication with WifiService @@ -1824,11 +1832,8 @@ public class WifiP2pManager { * @hide */ public Messenger getMessenger(Binder binder) { - try { - return mService.getMessenger(binder); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + // No way to determine package name in this case. + return getMessenger(binder, null); } /** diff --git a/wifi/tests/src/android/net/wifi/FakeKeys.java b/wifi/tests/src/android/net/wifi/FakeKeys.java index c0d60c33f99c..641b891a1f4d 100644 --- a/wifi/tests/src/android/net/wifi/FakeKeys.java +++ b/wifi/tests/src/android/net/wifi/FakeKeys.java @@ -214,6 +214,35 @@ public class FakeKeys { }; public static final PrivateKey RSA_KEY1 = loadPrivateRSAKey(FAKE_RSA_KEY_1); + private static final String CLIENT_SUITE_B_RSA3072_CERT_STRING = + "-----BEGIN CERTIFICATE-----\n" + + "MIIERzCCAq8CFDopjyNgaj+c2TN2k06h7okEWpHJMA0GCSqGSIb3DQEBDAUAMF4x\n" + + "CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEMMAoGA1UEBwwDTVRWMRAwDgYDVQQK\n" + + "DAdBbmRyb2lkMQ4wDAYDVQQLDAVXaS1GaTESMBAGA1UEAwwJdW5pdGVzdENBMB4X\n" + + "DTIwMDcyMTAyMjkxMVoXDTMwMDUzMDAyMjkxMVowYjELMAkGA1UEBhMCVVMxCzAJ\n" + + "BgNVBAgMAkNBMQwwCgYDVQQHDANNVFYxEDAOBgNVBAoMB0FuZHJvaWQxDjAMBgNV\n" + + "BAsMBVdpLUZpMRYwFAYDVQQDDA11bml0ZXN0Q2xpZW50MIIBojANBgkqhkiG9w0B\n" + + "AQEFAAOCAY8AMIIBigKCAYEAwSK3C5K5udtCKTnE14e8z2cZvwmB4Xe+a8+7QLud\n" + + "Hooc/lQzClgK4MbVUC0D3FE+U32C78SxKoTaRWtvPmNm+UaFT8KkwyUno/dv+2XD\n" + + "pd/zARQ+3FwAfWopAhEyCVSxwsCa+slQ4juRIMIuUC1Mm0NaptZyM3Tj/ICQEfpk\n" + + "o9qVIbiK6eoJMTkY8EWfAn7RTFdfR1OLuO0mVOjgLW9/+upYv6hZ19nAMAxw4QTJ\n" + + "x7lLwALX7B+tDYNEZHDqYL2zyvQWAj2HClere8QYILxkvktgBg2crEJJe4XbDH7L\n" + + "A3rrXmsiqf1ZbfFFEzK9NFqovL+qGh+zIP+588ShJFO9H/RDnDpiTnAFTWXQdTwg\n" + + "szSS0Vw2PB+JqEABAa9DeMvXT1Oy+NY3ItPHyy63nQZVI2rXANw4NhwS0Z6DF+Qs\n" + + "TNrj+GU7e4SG/EGR8SvldjYfQTWFLg1l/UT1hOOkQZwdsaW1zgKyeuiFB2KdMmbA\n" + + "Sq+Ux1L1KICo0IglwWcB/8nnAgMBAAEwDQYJKoZIhvcNAQEMBQADggGBAMYwJkNw\n" + + "BaCviKFmReDTMwWPRy4AMNViEeqAXgERwDEKwM7efjsaj5gctWfKsxX6UdLzkhgg\n" + + "6S/T6PxVWKzJ6l7SoOuTa6tMQOZp+h3R1mdfEQbw8B5cXBxZ+batzAai6Fiy1FKS\n" + + "/ka3INbcGfYuIYghfTrb4/NJKN06ZaQ1bpPwq0e4gN7800T2nbawvSf7r+8ZLcG3\n" + + "6bGCjRMwDSIipNvOwoj3TG315XC7TccX5difQ4sKOY+d2MkVJ3RiO0Ciw2ZbEW8d\n" + + "1FH5vUQJWnBUfSFznosGzLwH3iWfqlP+27jNE+qB2igEwCRFgVAouURx5ou43xuX\n" + + "qf6JkdI3HTJGLIWxkp7gOeln4dEaYzKjYw+P0VqJvKVqQ0IXiLjHgE0J9p0vgyD6\n" + + "HVVcP7U8RgqrbIjL1QgHU4KBhGi+WSUh/mRplUCNvHgcYdcHi/gHpj/j6ubwqIGV\n" + + "z4iSolAHYTmBWcLyE0NgpzE6ntp+53r2KaUJA99l2iGVzbWTwqPSm0XAVw==\n" + + "-----END CERTIFICATE-----\n"; + public static final X509Certificate CLIENT_SUITE_B_RSA3072_CERT = + loadCertificate(CLIENT_SUITE_B_RSA3072_CERT_STRING); + private static X509Certificate loadCertificate(String blob) { try { final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java index c2d0d6d81e84..254434b81f8f 100644 --- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java @@ -310,12 +310,6 @@ public class SoftApConfigurationTest { .build(); assertNull(band_6g_config.toWifiConfiguration()); - SoftApConfiguration sae_transition_config = new SoftApConfiguration.Builder() - .setPassphrase("secretsecret", - SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) - .build(); - - assertNull(sae_transition_config.toWifiConfiguration()); } @Test @@ -358,5 +352,16 @@ public class SoftApConfigurationTest { assertThat(wifiConfig_2g5g.apBand).isEqualTo(WifiConfiguration.AP_BAND_ANY); assertThat(wifiConfig_2g5g.apChannel).isEqualTo(0); assertThat(wifiConfig_2g5g.hiddenSSID).isEqualTo(true); + + SoftApConfiguration softApConfig_sae_transition = new SoftApConfiguration.Builder() + .setPassphrase("secretsecret", + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) + .build(); + + WifiConfiguration wifiConfig_sae_transition = + softApConfig_sae_transition.toWifiConfiguration(); + assertThat(wifiConfig_sae_transition.getAuthType()) + .isEqualTo(WifiConfiguration.KeyMgmt.WPA2_PSK); + assertThat(wifiConfig_sae_transition.preSharedKey).isEqualTo("secretsecret"); } } diff --git a/wifi/tests/src/android/net/wifi/WifiClientTest.java b/wifi/tests/src/android/net/wifi/WifiClientTest.java index 42cab55305b9..7a3baf9ebaf2 100644 --- a/wifi/tests/src/android/net/wifi/WifiClientTest.java +++ b/wifi/tests/src/android/net/wifi/WifiClientTest.java @@ -16,8 +16,8 @@ package android.net.wifi; -import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals; -import static com.android.testutils.ParcelUtilsKt.assertParcelSane; +import static com.android.testutils.MiscAsserts.assertFieldCountEquals; +import static com.android.testutils.ParcelUtils.assertParcelSane; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java index 638efb9f14ee..8270d643ca65 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java @@ -23,6 +23,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import android.net.wifi.EAPConstants; +import android.net.wifi.FakeKeys; import android.net.wifi.hotspot2.pps.Credential; import android.net.wifi.hotspot2.pps.HomeSp; import android.os.Parcel; @@ -32,6 +34,11 @@ import androidx.test.filters.SmallTest; import org.junit.Test; import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -383,19 +390,39 @@ public class PasspointConfigurationTest { } /** - * Verify that the unique identifier generated is different for two instances with different - * HomeSp node + * Verify that the unique identifier generated is the same for two instances with different + * HomeSp node but same FQDN * * @throws Exception */ @Test - public void validateUniqueIdDifferentHomeSp() throws Exception { + public void validateUniqueIdDifferentHomeSpSameFqdn() throws Exception { PasspointConfiguration config1 = PasspointTestUtils.createConfig(); - // Modify config2's RCOIs to a different set of values + // Modify config2's RCOIs and friendly name to a different set of values PasspointConfiguration config2 = PasspointTestUtils.createConfig(); HomeSp homeSp = config2.getHomeSp(); homeSp.setRoamingConsortiumOis(new long[] {0xaa, 0xbb}); + homeSp.setFriendlyName("Some other name"); + config2.setHomeSp(homeSp); + + assertEquals(config1.getUniqueId(), config2.getUniqueId()); + } + + /** + * Verify that the unique identifier generated is different for two instances with the same + * HomeSp node but different FQDN + * + * @throws Exception + */ + @Test + public void validateUniqueIdSameHomeSpDifferentFqdn() throws Exception { + PasspointConfiguration config1 = PasspointTestUtils.createConfig(); + + // Modify config2's FQDN to a different value + PasspointConfiguration config2 = PasspointTestUtils.createConfig(); + HomeSp homeSp = config2.getHomeSp(); + homeSp.setFqdn("fqdn2.com"); config2.setHomeSp(homeSp); assertNotEquals(config1.getUniqueId(), config2.getUniqueId()); @@ -403,15 +430,15 @@ public class PasspointConfigurationTest { /** * Verify that the unique identifier generated is different for two instances with different - * Credential node + * SIM Credential node * * @throws Exception */ @Test - public void validateUniqueIdDifferentCredential() throws Exception { + public void validateUniqueIdDifferentSimCredential() throws Exception { PasspointConfiguration config1 = PasspointTestUtils.createConfig(); - // Modify config2's RCOIs to a different set of values + // Modify config2's realm and SIM credential to a different set of values PasspointConfiguration config2 = PasspointTestUtils.createConfig(); Credential credential = config2.getCredential(); credential.setRealm("realm2.example.com"); @@ -422,6 +449,157 @@ public class PasspointConfigurationTest { } /** + * Verify that the unique identifier generated is different for two instances with different + * Realm in the Credential node + * + * @throws Exception + */ + @Test + public void validateUniqueIdDifferentRealm() throws Exception { + PasspointConfiguration config1 = PasspointTestUtils.createConfig(); + + // Modify config2's realm to a different set of values + PasspointConfiguration config2 = PasspointTestUtils.createConfig(); + Credential credential = config2.getCredential(); + credential.setRealm("realm2.example.com"); + config2.setCredential(credential); + + assertNotEquals(config1.getUniqueId(), config2.getUniqueId()); + } + + /** + * Verify that the unique identifier generated is the same for two instances with different + * password and same username in the User Credential node + * + * @throws Exception + */ + @Test + public void validateUniqueIdSameUserInUserCredential() throws Exception { + PasspointConfiguration config1 = PasspointTestUtils.createConfig(); + Credential credential = createCredentialWithUserCredential("user", "passwd"); + config1.setCredential(credential); + + // Modify config2's Passpowrd to a different set of values + PasspointConfiguration config2 = PasspointTestUtils.createConfig(); + credential = createCredentialWithUserCredential("user", "newpasswd"); + config2.setCredential(credential); + + assertEquals(config1.getUniqueId(), config2.getUniqueId()); + } + + /** + * Verify that the unique identifier generated is different for two instances with different + * username in the User Credential node + * + * @throws Exception + */ + @Test + public void validateUniqueIdDifferentUserCredential() throws Exception { + PasspointConfiguration config1 = PasspointTestUtils.createConfig(); + Credential credential = createCredentialWithUserCredential("user", "passwd"); + config1.setCredential(credential); + + // Modify config2's username to a different value + PasspointConfiguration config2 = PasspointTestUtils.createConfig(); + credential = createCredentialWithUserCredential("user2", "passwd"); + config2.setCredential(credential); + + assertNotEquals(config1.getUniqueId(), config2.getUniqueId()); + } + + /** + * Verify that the unique identifier generated is different for two instances with different + * Cert Credential node + * + * @throws Exception + */ + @Test + public void validateUniqueIdDifferentCertCredential() throws Exception { + PasspointConfiguration config1 = PasspointTestUtils.createConfig(); + Credential credential = createCredentialWithCertificateCredential(true, true); + config1.setCredential(credential); + + // Modify config2's cert credential to a different set of values + PasspointConfiguration config2 = PasspointTestUtils.createConfig(); + credential = createCredentialWithCertificateCredential(false, false); + config2.setCredential(credential); + + assertNotEquals(config1.getUniqueId(), config2.getUniqueId()); + } + + /** + * Helper function for generating certificate credential for testing. + * + * @return {@link Credential} + */ + private static Credential createCredentialWithCertificateCredential(Boolean useCaCert0, + Boolean useCert0) + throws NoSuchAlgorithmException, CertificateEncodingException { + Credential.CertificateCredential certCred = new Credential.CertificateCredential(); + certCred.setCertType("x509v3"); + if (useCert0) { + certCred.setCertSha256Fingerprint( + MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded())); + } else { + certCred.setCertSha256Fingerprint(MessageDigest.getInstance("SHA-256") + .digest(FakeKeys.CLIENT_SUITE_B_RSA3072_CERT.getEncoded())); + } + return createCredential(null, certCred, null, new X509Certificate[] {FakeKeys.CLIENT_CERT}, + FakeKeys.RSA_KEY1, useCaCert0 ? FakeKeys.CA_CERT0 : FakeKeys.CA_CERT1); + } + + /** + * Helper function for generating user credential for testing. + * + * @return {@link Credential} + */ + private static Credential createCredentialWithUserCredential(String username, String password) { + Credential.UserCredential userCred = new Credential.UserCredential(); + userCred.setUsername(username); + userCred.setPassword(password); + userCred.setMachineManaged(true); + userCred.setAbleToShare(true); + userCred.setSoftTokenApp("TestApp"); + userCred.setEapType(EAPConstants.EAP_TTLS); + userCred.setNonEapInnerMethod("MS-CHAP"); + return createCredential(userCred, null, null, null, null, FakeKeys.CA_CERT0); + } + + /** + * Helper function for generating Credential for testing. + * + * @param userCred Instance of UserCredential + * @param certCred Instance of CertificateCredential + * @param simCred Instance of SimCredential + * @param clientCertificateChain Chain of client certificates + * @param clientPrivateKey Client private key + * @param caCerts CA certificates + * @return {@link Credential} + */ + private static Credential createCredential(Credential.UserCredential userCred, + Credential.CertificateCredential certCred, + Credential.SimCredential simCred, + X509Certificate[] clientCertificateChain, PrivateKey clientPrivateKey, + X509Certificate... caCerts) { + Credential cred = new Credential(); + cred.setCreationTimeInMillis(123455L); + cred.setExpirationTimeInMillis(2310093L); + cred.setRealm("realm"); + cred.setCheckAaaServerCertStatus(true); + cred.setUserCredential(userCred); + cred.setCertCredential(certCred); + cred.setSimCredential(simCred); + if (caCerts != null && caCerts.length == 1) { + cred.setCaCertificate(caCerts[0]); + } else { + cred.setCaCertificates(caCerts); + } + cred.setClientCertificateChain(clientCertificateChain); + cred.setClientPrivateKey(clientPrivateKey); + return cred; + } + + /** * Verify that the unique identifier API generates an exception if HomeSP is not initialized. * * @throws Exception diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java index 829d8f0a9a3a..a44df40a8e97 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java @@ -593,10 +593,10 @@ public class CredentialTest { } /** - * Verify that unique identifiers are different for a credential with different values + * Verify that unique identifiers are different for a credential with different username */ @Test - public void testUniqueIdDifferentForUserCredentialsWithDifferentValues() throws Exception { + public void testUniqueIdDifferentForUserCredentialsWithDifferentUsername() throws Exception { Credential userCred1 = createCredentialWithUserCredential(); Credential userCred2 = createCredentialWithUserCredential(); userCred2.getUserCredential().setUsername("anotheruser"); @@ -605,7 +605,24 @@ public class CredentialTest { } /** - * Verify that unique identifiers are different for a credential with different values + * Verify that unique identifiers are different for a credential with different password and + * other values other than username + */ + @Test + public void testUniqueIdSameForUserCredentialsWithDifferentPassword() throws Exception { + Credential userCred1 = createCredentialWithUserCredential(); + Credential userCred2 = createCredentialWithUserCredential(); + userCred2.getUserCredential().setPassword("someotherpassword!"); + userCred2.getUserCredential().setMachineManaged(false); + userCred2.getUserCredential().setAbleToShare(false); + userCred2.getUserCredential().setSoftTokenApp("TestApp2"); + userCred2.getUserCredential().setNonEapInnerMethod("PAP"); + + assertEquals(userCred1.getUniqueId(), userCred2.getUniqueId()); + } + + /** + * Verify that unique identifiers are different for a cert credential with different values */ @Test public void testUniqueIdDifferentForCertCredentialsWithDifferentValues() throws Exception { |