summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java25
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java32
-rw-r--r--core/api/current.txt2
-rw-r--r--core/api/test-current.txt5
-rw-r--r--core/java/android/app/AppOpsManager.java252
-rw-r--r--core/java/android/app/SyncNotedAppOp.java54
-rw-r--r--core/java/android/appwidget/AppWidgetProviderInfo.java11
-rw-r--r--core/java/android/content/AttributionSource.java385
-rw-r--r--core/java/android/content/PermissionChecker.java502
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageUtils.java7
-rw-r--r--core/java/android/hardware/input/InputManager.java8
-rw-r--r--core/java/android/os/BinderProxy.java5
-rw-r--r--core/java/android/print/PrintAttributes.java4
-rw-r--r--core/java/android/util/JsonReader.java2
-rw-r--r--core/java/android/view/MotionEvent.java6
-rw-r--r--core/java/android/window/SplashScreenView.java20
-rw-r--r--core/java/com/android/internal/accessibility/AccessibilityShortcutController.java9
-rw-r--r--core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java12
-rw-r--r--core/java/com/android/internal/app/procstats/DumpUtils.java1
-rw-r--r--core/java/com/android/internal/app/procstats/ProcessState.java8
-rw-r--r--core/java/com/android/internal/compat/CompatibilityChangeConfig.java8
-rw-r--r--core/java/com/android/internal/os/BinderDeathDispatcher.java14
-rw-r--r--core/java/com/android/internal/os/TEST_MAPPING10
-rw-r--r--core/java/com/android/internal/util/StringPool.java77
-rw-r--r--core/jni/AndroidRuntime.cpp4
-rw-r--r--core/res/res/drawable/ic_accessibility_one_handed.xml25
-rw-r--r--core/res/res/drawable/ic_accessibility_one_handed_foreground.xml26
-rwxr-xr-xcore/res/res/values-es-rMX/donottranslate-cldr.xml8
-rw-r--r--core/res/res/values/strings.xml7
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java62
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/StringPoolTest.java45
-rw-r--r--graphics/java/android/graphics/drawable/Icon.java4
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java137
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java69
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java43
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java69
-rw-r--r--media/java/android/media/SoundPool.java13
-rw-r--r--native/graphics/jni/imagedecoder.cpp2
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java7
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml2
-rw-r--r--packages/SystemUI/docs/QS-QQS.pngbin77843 -> 79618 bytes
-rw-r--r--packages/SystemUI/docs/qs-tiles.md14
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java15
-rw-r--r--packages/SystemUI/res-keyguard/layout/qs_media_divider.xml7
-rw-r--r--packages/SystemUI/res/layout/ongoing_call_chip.xml7
-rw-r--r--packages/SystemUI/res/layout/qs_carrier_group.xml3
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl.xml2
-rw-r--r--packages/SystemUI/res/layout/qs_panel.xml2
-rw-r--r--packages/SystemUI/res/layout/qs_tile_label.xml82
-rw-r--r--packages/SystemUI/res/layout/qs_tile_side_icon.xml41
-rw-r--r--packages/SystemUI/res/layout/quick_qs_status_icons.xml13
-rw-r--r--packages/SystemUI/res/layout/quick_settings_security_footer.xml2
-rw-r--r--packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml1
-rw-r--r--packages/SystemUI/res/layout/wallet_card_view.xml11
-rw-r--r--packages/SystemUI/res/values-land/dimens.xml2
-rw-r--r--packages/SystemUI/res/values/colors.xml1
-rw-r--r--packages/SystemUI/res/values/dimens.xml8
-rw-r--r--packages/SystemUI/res/values/strings.xml9
-rw-r--r--packages/SystemUI/res/values/styles.xml15
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt71
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/ButtonRelativeLayout.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java416
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java200
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt215
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt538
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt87
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewInfo.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileBaseViewTest.kt)106
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt161
-rw-r--r--services/core/java/com/android/server/SystemServerInitThreadPool.java2
-rw-r--r--services/core/java/com/android/server/SystemService.java10
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java22
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java35
-rw-r--r--services/core/java/com/android/server/am/ProcessErrorStateRecord.java29
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java5
-rw-r--r--services/core/java/com/android/server/apphibernation/AppHibernationService.java65
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java65
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java4
-rw-r--r--services/core/java/com/android/server/content/ContentService.java8
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java45
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java28
-rw-r--r--services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java457
-rw-r--r--services/core/java/com/android/server/sensors/SensorService.java64
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimator.java10
-rw-r--r--services/core/java/com/android/server/wm/Task.java20
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java85
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java13
-rw-r--r--services/core/jni/Android.bp1
-rw-r--r--services/core/jni/com_android_server_SystemServer.cpp13
-rw-r--r--services/core/jni/com_android_server_sensor_SensorService.cpp49
-rw-r--r--services/core/jni/onload.cpp2
-rw-r--r--services/java/com/android/server/SystemServer.java25
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java2
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java7
-rw-r--r--tests/ActivityViewTest/Android.bp15
-rw-r--r--tests/ActivityViewTest/AndroidManifest.xml67
-rw-r--r--tests/ActivityViewTest/res/layout/activity_view_activity.xml47
-rw-r--r--tests/ActivityViewTest/res/layout/activity_view_main_activity.xml50
-rw-r--r--tests/ActivityViewTest/res/layout/activity_view_resize_activity.xml51
-rw-r--r--tests/ActivityViewTest/res/layout/activity_view_scroll_activity.xml57
-rw-r--r--tests/ActivityViewTest/res/layout/activity_view_test_activity.xml70
-rw-r--r--tests/ActivityViewTest/res/layout/activity_view_visibility_activity.xml46
-rw-r--r--tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java56
-rw-r--r--tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java42
-rw-r--r--tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewResizeActivity.java79
-rw-r--r--tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewScrollActivity.java44
-rw-r--r--tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java116
-rw-r--r--tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewVisibilityActivity.java75
-rw-r--r--tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java2
-rw-r--r--tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java3
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java25
166 files changed, 3407 insertions, 3437 deletions
diff --git a/Android.bp b/Android.bp
index acf86a0cfa2d..0ffafc6dcef2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -326,6 +326,7 @@ java_defaults {
"tv_tuner_resource_manager_aidl_interface-java",
"soundtrigger_middleware-aidl-java",
"modules-utils-os",
+ "framework-permission-aidl-java",
],
}
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index f96fc835b064..b62ece6759c6 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -236,7 +236,21 @@ public class PowerExemptionManager {
* @hide
*/
public static final int REASON_BLUETOOTH_BROADCAST = 203;
-
+ /**
+ * Broadcast {@link android.content.Intent#ACTION_TIMEZONE_CHANGED}
+ * @hide
+ */
+ public static final int REASON_TIMEZONE_CHANGED = 204;
+ /**
+ * Broadcast {@link android.content.Intent#ACTION_TIME_CHANGED}
+ * @hide
+ */
+ public static final int REASON_TIME_CHANGED = 205;
+ /**
+ * Broadcast {@link android.content.Intent#ACTION_LOCALE_CHANGED}
+ * @hide
+ */
+ public static final int REASON_LOCALE_CHANGED = 206;
/* Reason code range 300-399 are reserved for other internal reasons */
/**
* Device idle system allow list, including EXCEPT-IDLE
@@ -369,6 +383,9 @@ public class PowerExemptionManager {
REASON_PRE_BOOT_COMPLETED,
REASON_LOCKED_BOOT_COMPLETED,
REASON_BLUETOOTH_BROADCAST,
+ REASON_TIMEZONE_CHANGED,
+ REASON_TIME_CHANGED,
+ REASON_LOCALE_CHANGED,
REASON_SYSTEM_ALLOW_LISTED,
REASON_ALARM_MANAGER_ALARM_CLOCK,
REASON_ALARM_MANAGER_WHILE_IDLE,
@@ -641,6 +658,12 @@ public class PowerExemptionManager {
return "LOCKED_BOOT_COMPLETED";
case REASON_BLUETOOTH_BROADCAST:
return "BLUETOOTH_BROADCAST";
+ case REASON_TIMEZONE_CHANGED:
+ return "TIMEZONE_CHANGED";
+ case REASON_TIME_CHANGED:
+ return "TIME_CHANGED";
+ case REASON_LOCALE_CHANGED:
+ return "LOCALE_CHANGED";
case REASON_SYSTEM_ALLOW_LISTED:
return "SYSTEM_ALLOW_LISTED";
case REASON_ALARM_MANAGER_ALARM_CLOCK:
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 64686a1682f0..70d0d5d18118 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -30,6 +30,7 @@ import static android.app.AlarmManager.INTERVAL_HOUR;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.PowerWhitelistManager.REASON_ALARM_MANAGER_WHILE_IDLE;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
@@ -81,6 +82,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelableException;
+import android.os.PowerExemptionManager;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -118,6 +120,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.os.BinderDeathDispatcher;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.LocalLog;
@@ -202,6 +205,8 @@ public class AlarmManagerService extends SystemService {
.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ private static final BinderDeathDispatcher<IAlarmListener> sListenerDeathDispatcher =
+ new BinderDeathDispatcher<>();
final LocalLog mLog = new LocalLog(TAG);
AppOpsManager mAppOps;
@@ -292,6 +297,7 @@ public class AlarmManagerService extends SystemService {
BroadcastOptions mOptsWithFgs = BroadcastOptions.makeBasic();
BroadcastOptions mOptsWithoutFgs = BroadcastOptions.makeBasic();
+ BroadcastOptions mOptsTimeBroadcast = BroadcastOptions.makeBasic();
// TODO(b/172085676): Move inside alarm store.
private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
@@ -1786,7 +1792,12 @@ public class AlarmManagerService extends SystemService {
| Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
intent.putExtra(Intent.EXTRA_TIMEZONE, zone.getID());
- getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+ mOptsTimeBroadcast.setTemporaryAppAllowlist(
+ mActivityManagerInternal.getBootTimeTempAllowListDuration(),
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ PowerExemptionManager.REASON_TIMEZONE_CHANGED, "");
+ getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
+ null /* receiverPermission */, mOptsTimeBroadcast.toBundle());
}
}
@@ -1809,9 +1820,8 @@ public class AlarmManagerService extends SystemService {
}
if (directReceiver != null) {
- try {
- directReceiver.asBinder().linkToDeath(mListenerDeathRecipient, 0);
- } catch (RemoteException e) {
+ if (sListenerDeathDispatcher.linkToDeath(directReceiver, mListenerDeathRecipient)
+ <= 0) {
Slog.w(TAG, "Dropping unreachable alarm listener " + listenerTag);
return;
}
@@ -2824,6 +2834,12 @@ public class AlarmManagerService extends SystemService {
pw.println();
}
+ pw.println("Listener death dispatcher state:");
+ pw.increaseIndent();
+ sListenerDeathDispatcher.dump(pw);
+ pw.println();
+ pw.decreaseIndent();
+
if (mLog.dump(pw, "Recent problems:")) {
pw.println();
}
@@ -3929,8 +3945,12 @@ public class AlarmManagerService extends SystemService {
| Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
- getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
-
+ mOptsTimeBroadcast.setTemporaryAppAllowlist(
+ mActivityManagerInternal.getBootTimeTempAllowListDuration(),
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ PowerExemptionManager.REASON_TIME_CHANGED, "");
+ getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
+ null /* receiverPermission */, mOptsTimeBroadcast.toBundle());
// The world has changed on us, so we need to re-evaluate alarms
// regardless of whether the kernel has told us one went off.
result |= IS_WAKEUP_MASK;
diff --git a/core/api/current.txt b/core/api/current.txt
index c2cb78ec32cd..82264c177248 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -32950,6 +32950,7 @@ package android.print {
field public static final android.print.PrintAttributes.MediaSize JPN_HAGAKI;
field public static final android.print.PrintAttributes.MediaSize JPN_KAHU;
field public static final android.print.PrintAttributes.MediaSize JPN_KAKU2;
+ field @NonNull public static final android.print.PrintAttributes.MediaSize JPN_OE_PHOTO_L;
field public static final android.print.PrintAttributes.MediaSize JPN_OUFUKU;
field public static final android.print.PrintAttributes.MediaSize JPN_YOU4;
field @NonNull public static final android.print.PrintAttributes.MediaSize NA_ARCH_A;
@@ -32971,7 +32972,6 @@ package android.print {
field public static final android.print.PrintAttributes.MediaSize NA_QUARTO;
field @NonNull public static final android.print.PrintAttributes.MediaSize NA_SUPER_B;
field public static final android.print.PrintAttributes.MediaSize NA_TABLOID;
- field @NonNull public static final android.print.PrintAttributes.MediaSize OE_PHOTO_L;
field public static final android.print.PrintAttributes.MediaSize OM_DAI_PA_KAI;
field public static final android.print.PrintAttributes.MediaSize OM_JUURO_KU_KAI;
field public static final android.print.PrintAttributes.MediaSize OM_PA_KAI;
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 196799345efd..d366e35dd11a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -219,6 +219,7 @@ package android.app {
public class AppOpsManager {
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void addHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOps);
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void clearHistory();
+ method public static void collectNotedOpSync(@NonNull android.app.SyncNotedAppOp);
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void getHistoricalOpsFromDiskRaw(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
method public static int getNumOps();
method public boolean isOperationActive(int, int, String);
@@ -360,6 +361,10 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
}
+ public final class SyncNotedAppOp implements android.os.Parcelable {
+ ctor public SyncNotedAppOp(int, @IntRange(from=0L) int, @Nullable String, @NonNull String);
+ }
+
public class TaskInfo {
method public boolean containsLaunchCookie(@NonNull android.os.IBinder);
method @NonNull public android.content.res.Configuration getConfiguration();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 75a38c2380b9..4975fc222ef3 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2783,14 +2783,24 @@ public class AppOpsManager {
private static final ThreadLocal<Integer> sBinderThreadCallingUid = new ThreadLocal<>();
/**
- * If a thread is currently executing a two-way binder transaction, this stores the op-codes of
- * the app-ops that were noted during this transaction.
+ * Optimization: we need to propagate to IPCs whether the current thread is collecting
+ * app ops but using only the thread local above is too slow as it requires a map lookup
+ * on every IPC. We add this static var that is lockless and stores an OR-ed mask of the
+ * thread id's currently collecting ops, thus reducing the map lookup to a simple bit
+ * operation except the extremely unlikely case when threads with overlapping id bits
+ * execute op collecting ops.
+ */
+ private static volatile long sThreadsListeningForOpNotedInBinderTransaction = 0L;
+
+ /**
+ * If a thread is currently executing a two-way binder transaction, this stores the
+ * ops that were noted blaming any app (the caller, the caller of the caller, etc).
*
* @see #getNotedOpCollectionMode
* @see #collectNotedOpSync
*/
- private static final ThreadLocal<ArrayMap<String, long[]>> sAppOpsNotedInThisBinderTransaction =
- new ThreadLocal<>();
+ private static final ThreadLocal<ArrayMap<String, ArrayMap<String, long[]>>>
+ sAppOpsNotedInThisBinderTransaction = new ThreadLocal<>();
/** Whether noting for an appop should be collected */
private static final @ShouldCollectNoteOp byte[] sAppOpsToNote = new byte[_NUM_OP];
@@ -6844,7 +6854,7 @@ public class AppOpsManager {
writeLongSparseLongArrayToParcel(mAccessCount, parcel);
writeLongSparseLongArrayToParcel(mRejectCount, parcel);
writeLongSparseLongArrayToParcel(mAccessDuration, parcel);
- writeDiscreteAccessArrayToParcel(mDiscreteAccesses, parcel);
+ writeDiscreteAccessArrayToParcel(mDiscreteAccesses, parcel, flags);
}
@Override
@@ -8105,7 +8115,7 @@ public class AppOpsManager {
SyncNotedAppOp syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
- if (syncOp.getOpMode()== MODE_ALLOWED) {
+ if (syncOp.getOpMode() == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
collectNotedOpForSelf(syncOp);
} else if (collectionMode == COLLECT_SYNC) {
@@ -8872,70 +8882,11 @@ public class AppOpsManager {
* @hide
*/
public static void startNotedAppOpsCollection(int callingUid) {
+ sThreadsListeningForOpNotedInBinderTransaction |= Thread.currentThread().getId();
sBinderThreadCallingUid.set(callingUid);
}
/**
- * State of a temporarily paused noted app-ops collection.
- *
- * @see #pauseNotedAppOpsCollection()
- *
- * @hide
- */
- public static class PausedNotedAppOpsCollection {
- final int mUid;
- final @Nullable ArrayMap<String, long[]> mCollectedNotedAppOps;
-
- PausedNotedAppOpsCollection(int uid, @Nullable ArrayMap<String,
- long[]> collectedNotedAppOps) {
- mUid = uid;
- mCollectedNotedAppOps = collectedNotedAppOps;
- }
- }
-
- /**
- * Temporarily suspend collection of noted app-ops when binder-thread calls into the other
- * process. During such a call there might be call-backs coming back on the same thread which
- * should not be accounted to the current collection.
- *
- * @return a state needed to resume the collection
- *
- * @hide
- */
- public static @Nullable PausedNotedAppOpsCollection pauseNotedAppOpsCollection() {
- Integer previousUid = sBinderThreadCallingUid.get();
- if (previousUid != null) {
- ArrayMap<String, long[]> previousCollectedNotedAppOps =
- sAppOpsNotedInThisBinderTransaction.get();
-
- sBinderThreadCallingUid.remove();
- sAppOpsNotedInThisBinderTransaction.remove();
-
- return new PausedNotedAppOpsCollection(previousUid, previousCollectedNotedAppOps);
- }
-
- return null;
- }
-
- /**
- * Resume a collection paused via {@link #pauseNotedAppOpsCollection}.
- *
- * @param prevCollection The state of the previous collection
- *
- * @hide
- */
- public static void resumeNotedAppOpsCollection(
- @Nullable PausedNotedAppOpsCollection prevCollection) {
- if (prevCollection != null) {
- sBinderThreadCallingUid.set(prevCollection.mUid);
-
- if (prevCollection.mCollectedNotedAppOps != null) {
- sAppOpsNotedInThisBinderTransaction.set(prevCollection.mCollectedNotedAppOps);
- }
- }
- }
-
- /**
* Finish collection of noted appops on this thread.
*
* <p>Called at the end of a two way binder transaction.
@@ -8946,6 +8897,7 @@ public class AppOpsManager {
*/
public static void finishNotedAppOpsCollection() {
sBinderThreadCallingUid.remove();
+ sThreadsListeningForOpNotedInBinderTransaction &= ~Thread.currentThread().getId();
sAppOpsNotedInThisBinderTransaction.remove();
}
@@ -8970,28 +8922,52 @@ public class AppOpsManager {
* <p> Delivered to caller via {@link #prefixParcelWithAppOpsIfNeeded}
*
* @param syncOp the op and attribution tag to note for
+ *
+ * @hide
+ */
+ @TestApi
+ public static void collectNotedOpSync(@NonNull SyncNotedAppOp syncOp) {
+ collectNotedOpSync(sOpStrToOp.get(syncOp.getOp()), syncOp.getAttributionTag(),
+ syncOp.getPackageName());
+ }
+
+ /**
+ * Collect a noted op when inside of a two-way binder call.
+ *
+ * <p> Delivered to caller via {@link #prefixParcelWithAppOpsIfNeeded}
+ *
+ * @param code the op code to note for
+ * @param attributionTag the attribution tag to note for
+ * @param packageName the package to note for
*/
- private void collectNotedOpSync(@NonNull SyncNotedAppOp syncOp) {
+ private static void collectNotedOpSync(int code, @Nullable String attributionTag,
+ @NonNull String packageName) {
// If this is inside of a two-way binder call:
// We are inside of a two-way binder call. Delivered to caller via
// {@link #prefixParcelWithAppOpsIfNeeded}
- int op = sOpStrToOp.get(syncOp.getOp());
- ArrayMap<String, long[]> appOpsNoted = sAppOpsNotedInThisBinderTransaction.get();
+ ArrayMap<String, ArrayMap<String, long[]>> appOpsNoted =
+ sAppOpsNotedInThisBinderTransaction.get();
if (appOpsNoted == null) {
appOpsNoted = new ArrayMap<>(1);
sAppOpsNotedInThisBinderTransaction.set(appOpsNoted);
}
- long[] appOpsNotedForAttribution = appOpsNoted.get(syncOp.getAttributionTag());
+ ArrayMap<String, long[]> packageAppOpsNotedForAttribution = appOpsNoted.get(packageName);
+ if (packageAppOpsNotedForAttribution == null) {
+ packageAppOpsNotedForAttribution = new ArrayMap<>(1);
+ appOpsNoted.put(packageName, packageAppOpsNotedForAttribution);
+ }
+
+ long[] appOpsNotedForAttribution = packageAppOpsNotedForAttribution.get(attributionTag);
if (appOpsNotedForAttribution == null) {
appOpsNotedForAttribution = new long[2];
- appOpsNoted.put(syncOp.getAttributionTag(), appOpsNotedForAttribution);
+ packageAppOpsNotedForAttribution.put(attributionTag, appOpsNotedForAttribution);
}
- if (op < 64) {
- appOpsNotedForAttribution[0] |= 1L << op;
+ if (code < 64) {
+ appOpsNotedForAttribution[0] |= 1L << code;
} else {
- appOpsNotedForAttribution[1] |= 1L << (op - 64);
+ appOpsNotedForAttribution[1] |= 1L << (code - 64);
}
}
@@ -9045,9 +9021,7 @@ public class AppOpsManager {
}
}
- Integer binderUid = sBinderThreadCallingUid.get();
-
- if (binderUid != null && binderUid == uid) {
+ if (isListeningForOpNotedInBinderTransaction()) {
return COLLECT_SYNC;
} else {
return COLLECT_ASYNC;
@@ -9064,21 +9038,31 @@ public class AppOpsManager {
*
* @hide
*/
+ // TODO (b/186872903) Refactor how sync noted ops are propagaged.
public static void prefixParcelWithAppOpsIfNeeded(@NonNull Parcel p) {
- ArrayMap<String, long[]> notedAppOps = sAppOpsNotedInThisBinderTransaction.get();
+ final ArrayMap<String, ArrayMap<String, long[]>> notedAppOps =
+ sAppOpsNotedInThisBinderTransaction.get();
if (notedAppOps == null) {
return;
}
p.writeInt(Parcel.EX_HAS_NOTED_APPOPS_REPLY_HEADER);
- int numAttributionWithNotesAppOps = notedAppOps.size();
- p.writeInt(numAttributionWithNotesAppOps);
+ final int packageCount = notedAppOps.size();
+ p.writeInt(packageCount);
- for (int i = 0; i < numAttributionWithNotesAppOps; i++) {
+ for (int i = 0; i < packageCount; i++) {
p.writeString(notedAppOps.keyAt(i));
- p.writeLong(notedAppOps.valueAt(i)[0]);
- p.writeLong(notedAppOps.valueAt(i)[1]);
+
+ final ArrayMap<String, long[]> notedTagAppOps = notedAppOps.valueAt(i);
+ final int tagCount = notedTagAppOps.size();
+ p.writeInt(tagCount);
+
+ for (int j = 0; j < tagCount; j++) {
+ p.writeString(notedTagAppOps.keyAt(j));
+ p.writeLong(notedTagAppOps.valueAt(j)[0]);
+ p.writeLong(notedTagAppOps.valueAt(j)[1]);
+ }
}
}
@@ -9093,36 +9077,57 @@ public class AppOpsManager {
* @hide
*/
public static void readAndLogNotedAppops(@NonNull Parcel p) {
- int numAttributionsWithNotedAppOps = p.readInt();
+ final int packageCount = p.readInt();
+ if (packageCount <= 0) {
+ return;
+ }
+
+ final String myPackageName = ActivityThread.currentPackageName();
+ if (myPackageName == null) {
+ return;
+ }
+
+ synchronized (sLock) {
+ for (int i = 0; i < packageCount; i++) {
+ final String packageName = p.readString();
- for (int i = 0; i < numAttributionsWithNotedAppOps; i++) {
- String attributionTag = p.readString();
- long[] rawNotedAppOps = new long[2];
- rawNotedAppOps[0] = p.readLong();
- rawNotedAppOps[1] = p.readLong();
+ final int tagCount = p.readInt();
+ for (int j = 0; j < tagCount; j++) {
+ final String attributionTag = p.readString();
+ final long[] rawNotedAppOps = new long[2];
+ rawNotedAppOps[0] = p.readLong();
+ rawNotedAppOps[1] = p.readLong();
- if (rawNotedAppOps[0] != 0 || rawNotedAppOps[1] != 0) {
- BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps);
+ if (rawNotedAppOps[0] == 0 && rawNotedAppOps[1] == 0) {
+ continue;
+ }
- synchronized (sLock) {
+ final BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps);
for (int code = notedAppOps.nextSetBit(0); code != -1;
code = notedAppOps.nextSetBit(code + 1)) {
- if (sOnOpNotedCallback != null) {
- sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, attributionTag));
- } else {
- String message = getFormattedStackTrace();
- sUnforwardedOps.add(
- new AsyncNotedAppOp(code, Process.myUid(), attributionTag,
- message, System.currentTimeMillis()));
- if (sUnforwardedOps.size() > MAX_UNFORWARDED_OPS) {
- sUnforwardedOps.remove(0);
+ if (myPackageName.equals(packageName)) {
+ if (sOnOpNotedCallback != null) {
+ sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code,
+ attributionTag, packageName));
+ } else {
+ String message = getFormattedStackTrace();
+ sUnforwardedOps.add(new AsyncNotedAppOp(code, Process.myUid(),
+ attributionTag, message, System.currentTimeMillis()));
+ if (sUnforwardedOps.size() > MAX_UNFORWARDED_OPS) {
+ sUnforwardedOps.remove(0);
+ }
}
+ } else if (isListeningForOpNotedInBinderTransaction()) {
+ collectNotedOpSync(code, attributionTag, packageName);
+ }
+ }
+ for (int code = notedAppOps.nextSetBit(0); code != -1;
+ code = notedAppOps.nextSetBit(code + 1)) {
+ if (myPackageName.equals(packageName)) {
+ sMessageCollector.onNoted(new SyncNotedAppOp(code,
+ attributionTag, packageName));
}
}
- }
- for (int code = notedAppOps.nextSetBit(0); code != -1;
- code = notedAppOps.nextSetBit(code + 1)) {
- sMessageCollector.onNoted(new SyncNotedAppOp(code, attributionTag));
}
}
}
@@ -9229,7 +9234,17 @@ public class AppOpsManager {
* @hide
*/
public static boolean isListeningForOpNoted() {
- return sOnOpNotedCallback != null || isCollectingStackTraces();
+ return sOnOpNotedCallback != null || isListeningForOpNotedInBinderTransaction()
+ || isCollectingStackTraces();
+ }
+
+ /**
+ * @return whether we are in a binder transaction and collecting appops.
+ */
+ private static boolean isListeningForOpNotedInBinderTransaction() {
+ return (sThreadsListeningForOpNotedInBinderTransaction
+ & Thread.currentThread().getId()) != 0
+ && sBinderThreadCallingUid.get() != null;
}
/**
@@ -9696,29 +9711,16 @@ public class AppOpsManager {
}
private static void writeDiscreteAccessArrayToParcel(
- @Nullable List<AttributedOpEntry> array, @NonNull Parcel parcel) {
- if (array != null) {
- final int size = array.size();
- parcel.writeInt(size);
- for (int i = 0; i < size; i++) {
- array.get(i).writeToParcel(parcel, 0);
- }
- } else {
- parcel.writeInt(-1);
- }
+ @Nullable List<AttributedOpEntry> array, @NonNull Parcel parcel, int flags) {
+ ParceledListSlice<AttributedOpEntry> listSlice =
+ array == null ? null : new ParceledListSlice<>(array);
+ parcel.writeParcelable(listSlice, flags);
}
private static @Nullable List<AttributedOpEntry> readDiscreteAccessArrayFromParcel(
@NonNull Parcel parcel) {
- final int size = parcel.readInt();
- if (size < 0) {
- return null;
- }
- final List<AttributedOpEntry> array = new ArrayList<>(size);
- for (int i = 0; i < size; i++) {
- array.add(new AttributedOpEntry(parcel));
- }
- return array;
+ final ParceledListSlice<AttributedOpEntry> listSlice = parcel.readParcelable(null);
+ return listSlice == null ? null : listSlice.getList();
}
/**
diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java
index bc4e5436996d..32d889e81cb0 100644
--- a/core/java/android/app/SyncNotedAppOp.java
+++ b/core/java/android/app/SyncNotedAppOp.java
@@ -19,7 +19,9 @@ package android.app;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.os.Parcelable;
+import android.os.Process;
import com.android.internal.annotations.Immutable;
import com.android.internal.util.DataClass;
@@ -48,13 +50,19 @@ public final class SyncNotedAppOp implements Parcelable {
private final @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int mOpCode;
/** attributionTag of synchronous appop noted */
private final @Nullable String mAttributionTag;
+ /**
+ * The package this op applies to
+ * @hide
+ */
+ private final @NonNull String mPackageName;
/**
* Native code relies on parcel ordering, do not change
* @hide
*/
+ @TestApi
public SyncNotedAppOp(int opMode, @IntRange(from = 0L) int opCode,
- @Nullable String attributionTag) {
+ @Nullable String attributionTag, @NonNull String packageName) {
this.mOpCode = opCode;
com.android.internal.util.AnnotationValidations.validate(
IntRange.class, null, mOpCode,
@@ -62,6 +70,7 @@ public final class SyncNotedAppOp implements Parcelable {
"to", AppOpsManager._NUM_OP - 1);
this.mAttributionTag = attributionTag;
this.mOpMode = opMode;
+ this.mPackageName = packageName;
}
/**
@@ -73,7 +82,25 @@ public final class SyncNotedAppOp implements Parcelable {
* attributionTag of synchronous appop noted
*/
public SyncNotedAppOp(@IntRange(from = 0L) int opCode, @Nullable String attributionTag) {
- this(AppOpsManager.MODE_IGNORED, opCode, attributionTag);
+ this(AppOpsManager.MODE_IGNORED, opCode, attributionTag, ActivityThread
+ .currentPackageName());
+ }
+
+ /**
+ * Creates a new SyncNotedAppOp.
+ *
+ * @param opCode
+ * op code of synchronous appop noted
+ * @param attributionTag
+ * attributionTag of synchronous appop noted
+ * @param packageName
+ * The package this op applies to
+ *
+ * @hide
+ */
+ public SyncNotedAppOp(@IntRange(from = 0L) int opCode, @Nullable String attributionTag,
+ @NonNull String packageName) {
+ this(AppOpsManager.MODE_IGNORED, opCode, attributionTag, packageName);
}
/**
@@ -113,6 +140,16 @@ public final class SyncNotedAppOp implements Parcelable {
return mAttributionTag;
}
+ /**
+ * The package this op applies to
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
@Override
@DataClass.Generated.Member
public boolean equals(@Nullable Object o) {
@@ -128,7 +165,8 @@ public final class SyncNotedAppOp implements Parcelable {
return true
&& mOpMode == that.mOpMode
&& mOpCode == that.mOpCode
- && java.util.Objects.equals(mAttributionTag, that.mAttributionTag);
+ && java.util.Objects.equals(mAttributionTag, that.mAttributionTag)
+ && java.util.Objects.equals(mPackageName, that.mPackageName);
}
@Override
@@ -141,6 +179,7 @@ public final class SyncNotedAppOp implements Parcelable {
_hash = 31 * _hash + mOpMode;
_hash = 31 * _hash + mOpCode;
_hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName);
return _hash;
}
@@ -156,6 +195,7 @@ public final class SyncNotedAppOp implements Parcelable {
dest.writeInt(mOpMode);
dest.writeInt(mOpCode);
if (mAttributionTag != null) dest.writeString(mAttributionTag);
+ dest.writeString(mPackageName);
}
@Override
@@ -173,6 +213,7 @@ public final class SyncNotedAppOp implements Parcelable {
int opMode = in.readInt();
int opCode = in.readInt();
String attributionTag = (flg & 0x4) == 0 ? null : in.readString();
+ String packageName = in.readString();
this.mOpMode = opMode;
this.mOpCode = opCode;
@@ -181,6 +222,9 @@ public final class SyncNotedAppOp implements Parcelable {
"from", 0L,
"to", AppOpsManager._NUM_OP - 1);
this.mAttributionTag = attributionTag;
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
// onConstructed(); // You can define this method to get a callback
}
@@ -200,10 +244,10 @@ public final class SyncNotedAppOp implements Parcelable {
};
@DataClass.Generated(
- time = 1617317997768L,
+ time = 1619711733947L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/app/SyncNotedAppOp.java",
- inputSignatures = "private final int mOpMode\nprivate final @android.annotation.IntRange int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\npublic @android.annotation.NonNull java.lang.String getOp()\npublic int getOpMode()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genConstructor=false)")
+ inputSignatures = "private final int mOpMode\nprivate final @android.annotation.IntRange int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate final @android.annotation.NonNull java.lang.String mPackageName\npublic @android.annotation.NonNull java.lang.String getOp()\npublic int getOpMode()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genConstructor=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 3db18856d9a7..063ba1174cdc 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -455,13 +455,14 @@ public class AppWidgetProviderInfo implements Parcelable {
@Nullable
public final CharSequence loadDescription(@NonNull Context context) {
if (ResourceId.isValid(descriptionRes)) {
- return context.getPackageManager()
- .getText(
+ CharSequence description =
+ context.getPackageManager().getText(
providerInfo.packageName,
descriptionRes,
- providerInfo.applicationInfo)
- .toString()
- .trim();
+ providerInfo.applicationInfo);
+ if (description != null) {
+ return description.toString().trim();
+ }
}
return null;
}
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 2c155d5884ac..7ab731f15ad2 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -31,10 +31,9 @@ import android.permission.PermissionManager;
import android.util.ArraySet;
import com.android.internal.annotations.Immutable;
-import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.Objects;
import java.util.Set;
@@ -70,10 +69,10 @@ import java.util.Set;
* This is supported to handle cases where you don't have access to the caller's attribution
* source and you can directly use the {@link AttributionSource.Builder} APIs. However,
* if the data flows through more than two apps (more than you access the data for the
- * caller - which you cannot know ahead of time) you need to have a handle to the {@link
- * AttributionSource} for the calling app's context in order to create an attribution context.
- * This means you either need to have an API for the other app to send you its attribution
- * source or use a platform API that pipes the callers attribution source.
+ * caller) you need to have a handle to the {@link AttributionSource} for the calling app's
+ * context in order to create an attribution context. This means you either need to have an
+ * API for the other app to send you its attribution source or use a platform API that pipes
+ * the callers attribution source.
* <p>
* You cannot forge an attribution chain without the participation of every app in the
* attribution chain (aside of the special case mentioned above). To create an attribution
@@ -85,80 +84,11 @@ import java.util.Set;
* permission protected APIs since some app in the chain may not have the permission.
*/
@Immutable
-// TODO: Codegen doesn't properly verify the class if the parcelling is inner class
-// TODO: Codegen doesn't allow overriding the constructor to change its visibility
-// TODO: Codegen applies method level annotations to argument vs the generated member (@SystemApi)
-// TODO: Codegen doesn't properly read/write IBinder members
-// TODO: Codegen doesn't properly handle Set arguments
-// TODO: Codegen requires @SystemApi annotations on fields which breaks
-// android.signature.cts.api.AnnotationTest (need to update the test)
-// @DataClass(genEqualsHashCode = true, genConstructor = false, genBuilder = true)
public final class AttributionSource implements Parcelable {
- /**
- * @hide
- */
- static class RenouncedPermissionsParcelling implements Parcelling<Set<String>> {
-
- @Override
- public void parcel(Set<String> item, Parcel dest, int parcelFlags) {
- if (item == null) {
- dest.writeInt(-1);
- } else {
- dest.writeInt(item.size());
- for (String permission : item) {
- dest.writeString8(permission);
- }
- }
- }
-
- @Override
- public Set<String> unparcel(Parcel source) {
- final int size = source.readInt();
- if (size < 0) {
- return null;
- }
- final ArraySet<String> result = new ArraySet<>(size);
- for (int i = 0; i < size; i++) {
- result.add(source.readString8());
- }
- return result;
- }
- }
-
- /**
- * The UID that is accessing the permission protected data.
- */
- private final int mUid;
-
- /**
- * The package that is accessing the permission protected data.
- */
- private @Nullable String mPackageName = null;
+ private final @NonNull AttributionSourceState mAttributionSourceState;
- /**
- * The attribution tag of the app accessing the permission protected data.
- */
- private @Nullable String mAttributionTag = null;
-
- /**
- * Unique token for that source.
- *
- * @hide
- */
- private @Nullable IBinder mToken = null;
-
- /**
- * Permissions that should be considered revoked regardless if granted.
- *
- * @hide
- */
- @DataClass.ParcelWith(RenouncedPermissionsParcelling.class)
- private @Nullable Set<String> mRenouncedPermissions = null;
-
- /**
- * The next app to receive the permission protected data.
- */
- private @Nullable AttributionSource mNext = null;
+ private @Nullable AttributionSource mNextCached;
+ private @Nullable Set<String> mRenouncedPermissionsCached;
/** @hide */
@TestApi
@@ -171,8 +101,7 @@ public final class AttributionSource implements Parcelable {
@TestApi
public AttributionSource(int uid, @Nullable String packageName,
@Nullable String attributionTag, @Nullable AttributionSource next) {
- this(uid, packageName, attributionTag, /*token*/ null,
- /*renouncedPermissions*/ null, next);
+ this(uid, packageName, attributionTag, /*renouncedPermissions*/ null, next);
}
/** @hide */
@@ -180,8 +109,8 @@ public final class AttributionSource implements Parcelable {
public AttributionSource(int uid, @Nullable String packageName,
@Nullable String attributionTag, @Nullable Set<String> renouncedPermissions,
@Nullable AttributionSource next) {
- this(uid, packageName, attributionTag, /*token*/ null,
- renouncedPermissions, next);
+ this(uid, packageName, attributionTag, /*token*/ null, (renouncedPermissions != null)
+ ? renouncedPermissions.toArray(new String[0]) : null, next);
}
/** @hide */
@@ -191,16 +120,49 @@ public final class AttributionSource implements Parcelable {
/*token*/ null, /*renouncedPermissions*/ null, next);
}
+ AttributionSource(int uid, @Nullable String packageName, @Nullable String attributionTag,
+ @Nullable IBinder token, @Nullable String[] renouncedPermissions,
+ @Nullable AttributionSource next) {
+ mAttributionSourceState = new AttributionSourceState();
+ mAttributionSourceState.uid = uid;
+ mAttributionSourceState.packageName = packageName;
+ mAttributionSourceState.attributionTag = attributionTag;
+ mAttributionSourceState.token = token;
+ mAttributionSourceState.renouncedPermissions = renouncedPermissions;
+ mAttributionSourceState.next = (next != null) ? new AttributionSourceState[]
+ {next.mAttributionSourceState} : null;
+ }
+
+ AttributionSource(@NonNull Parcel in) {
+ this(AttributionSourceState.CREATOR.createFromParcel(in));
+ }
+
+ /** @hide */
+ public AttributionSource(@NonNull AttributionSourceState attributionSourceState) {
+ mAttributionSourceState = attributionSourceState;
+ }
+
/** @hide */
public AttributionSource withNextAttributionSource(@Nullable AttributionSource next) {
- return new AttributionSource(mUid, mPackageName, mAttributionTag, mToken,
- mRenouncedPermissions, next);
+ return new AttributionSource(getUid(), getPackageName(), getAttributionTag(),
+ getToken(), mAttributionSourceState.renouncedPermissions, next);
}
/** @hide */
public AttributionSource withToken(@Nullable IBinder token) {
- return new AttributionSource(mUid, mPackageName, mAttributionTag, token,
- mRenouncedPermissions, mNext);
+ return new AttributionSource(getUid(), getPackageName(), getAttributionTag(),
+ token, mAttributionSourceState.renouncedPermissions, getNext());
+ }
+
+ /** @hide */
+ public AttributionSource withPackageName(@Nullable String packageName) {
+ return new AttributionSource(getUid(), packageName, getAttributionTag(), getToken(),
+ mAttributionSourceState.renouncedPermissions, getNext());
+ }
+
+ /** @hide */
+ public @NonNull AttributionSourceState asState() {
+ return mAttributionSourceState;
}
/**
@@ -213,10 +175,9 @@ public final class AttributionSource implements Parcelable {
* from the caller.
*/
public void enforceCallingUid() {
- final int callingUid = Binder.getCallingUid();
- if (callingUid != Process.SYSTEM_UID && callingUid != mUid) {
- throw new SecurityException("Calling uid: " + callingUid
- + " doesn't match source uid: " + mUid);
+ if (!checkCallingUid()) {
+ throw new SecurityException("Calling uid: " + Binder.getCallingUid()
+ + " doesn't match source uid: " + mAttributionSourceState.uid);
}
// No need to check package as app ops manager does it already.
}
@@ -231,7 +192,8 @@ public final class AttributionSource implements Parcelable {
*/
public boolean checkCallingUid() {
final int callingUid = Binder.getCallingUid();
- if (callingUid != Process.SYSTEM_UID && callingUid != mUid) {
+ if (callingUid != Process.SYSTEM_UID
+ && callingUid != mAttributionSourceState.uid) {
return false;
}
// No need to check package as app ops manager does it already.
@@ -242,11 +204,12 @@ public final class AttributionSource implements Parcelable {
public String toString() {
if (Build.IS_DEBUGGABLE) {
return "AttributionSource { " +
- "uid = " + mUid + ", " +
- "packageName = " + mPackageName + ", " +
- "attributionTag = " + mAttributionTag + ", " +
- "token = " + mToken + ", " +
- "next = " + mNext +
+ "uid = " + mAttributionSourceState.uid + ", " +
+ "packageName = " + mAttributionSourceState.packageName + ", " +
+ "attributionTag = " + mAttributionSourceState.attributionTag + ", " +
+ "token = " + mAttributionSourceState.token + ", " +
+ "next = " + (mAttributionSourceState.next != null
+ ? mAttributionSourceState.next[0]: null) +
" }";
}
return super.toString();
@@ -258,8 +221,8 @@ public final class AttributionSource implements Parcelable {
* @hide
*/
public int getNextUid() {
- if (mNext != null) {
- return mNext.getUid();
+ if (mAttributionSourceState.next != null) {
+ return mAttributionSourceState.next[0].uid;
}
return Process.INVALID_UID;
}
@@ -270,8 +233,8 @@ public final class AttributionSource implements Parcelable {
* @hide
*/
public @Nullable String getNextPackageName() {
- if (mNext != null) {
- return mNext.getPackageName();
+ if (mAttributionSourceState.next != null) {
+ return mAttributionSourceState.next[0].packageName;
}
return null;
}
@@ -283,8 +246,8 @@ public final class AttributionSource implements Parcelable {
* @hide
*/
public @Nullable String getNextAttributionTag() {
- if (mNext != null) {
- return mNext.getAttributionTag();
+ if (mAttributionSourceState.next != null) {
+ return mAttributionSourceState.next[0].attributionTag;
}
return null;
}
@@ -297,8 +260,9 @@ public final class AttributionSource implements Parcelable {
* @return Whether this is a trusted source.
*/
public boolean isTrusted(@NonNull Context context) {
- return mToken != null && context.getSystemService(PermissionManager.class)
- .isRegisteredAttributionSource(this);
+ return mAttributionSourceState.token != null
+ && context.getSystemService(PermissionManager.class)
+ .isRegisteredAttributionSource(this);
}
/**
@@ -310,71 +274,36 @@ public final class AttributionSource implements Parcelable {
@RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
@NonNull
public Set<String> getRenouncedPermissions() {
- return CollectionUtils.emptyIfNull(mRenouncedPermissions);
- }
-
- @DataClass.Suppress({"setUid", "setToken"})
- static class BaseBuilder {}
-
-
-
-
-
-
- // Code below generated by codegen v1.0.22.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/AttributionSource.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
- /* package-private */ AttributionSource(
- int uid,
- @Nullable String packageName,
- @Nullable String attributionTag,
- @Nullable IBinder token,
- @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) @Nullable Set<String> renouncedPermissions,
- @Nullable AttributionSource next) {
- this.mUid = uid;
- this.mPackageName = packageName;
- this.mAttributionTag = attributionTag;
- this.mToken = token;
- this.mRenouncedPermissions = renouncedPermissions;
- com.android.internal.util.AnnotationValidations.validate(
- SystemApi.class, null, mRenouncedPermissions);
- com.android.internal.util.AnnotationValidations.validate(
- RequiresPermission.class, null, mRenouncedPermissions,
- "value", android.Manifest.permission.RENOUNCE_PERMISSIONS);
- this.mNext = next;
-
- // onConstructed(); // You can define this method to get a callback
+ if (mRenouncedPermissionsCached == null) {
+ if (mAttributionSourceState.renouncedPermissions != null) {
+ mRenouncedPermissionsCached = new ArraySet<>(
+ mAttributionSourceState.renouncedPermissions);
+ } else {
+ mRenouncedPermissionsCached = Collections.emptySet();
+ }
+ }
+ return mRenouncedPermissionsCached;
}
/**
* The UID that is accessing the permission protected data.
*/
public int getUid() {
- return mUid;
+ return mAttributionSourceState.uid;
}
/**
* The package that is accessing the permission protected data.
*/
public @Nullable String getPackageName() {
- return mPackageName;
+ return mAttributionSourceState.packageName;
}
/**
* The attribution tag of the app accessing the permission protected data.
*/
public @Nullable String getAttributionTag() {
- return mAttributionTag;
+ return mAttributionSourceState.attributionTag;
}
/**
@@ -383,113 +312,56 @@ public final class AttributionSource implements Parcelable {
* @hide
*/
public @Nullable IBinder getToken() {
- return mToken;
+ return mAttributionSourceState.token;
}
/**
* The next app to receive the permission protected data.
*/
public @Nullable AttributionSource getNext() {
- return mNext;
+ if (mNextCached == null && mAttributionSourceState.next != null) {
+ mNextCached = new AttributionSource(mAttributionSourceState.next[0]);
+ }
+ return mNextCached;
}
@Override
public boolean equals(@Nullable Object o) {
- // You can override field equality logic by defining either of the methods like:
- // boolean fieldNameEquals(AttributionSource other) { ... }
- // boolean fieldNameEquals(FieldType otherValue) { ... }
-
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- @SuppressWarnings("unchecked")
AttributionSource that = (AttributionSource) o;
- //noinspection PointlessBooleanExpression
- return true
- && mUid == that.mUid
- && Objects.equals(mPackageName, that.mPackageName)
- && Objects.equals(mAttributionTag, that.mAttributionTag)
- && Objects.equals(mToken, that.mToken)
- && Objects.equals(mRenouncedPermissions, that.mRenouncedPermissions)
- && Objects.equals(mNext, that.mNext);
+ return mAttributionSourceState.uid == that.mAttributionSourceState.uid
+ && Objects.equals(mAttributionSourceState.packageName,
+ that.mAttributionSourceState.packageName)
+ && Objects.equals(mAttributionSourceState.attributionTag,
+ that.mAttributionSourceState.attributionTag)
+ && Objects.equals(mAttributionSourceState.token,
+ that.mAttributionSourceState.token)
+ && Arrays.equals(mAttributionSourceState.renouncedPermissions,
+ that.mAttributionSourceState.renouncedPermissions)
+ && Objects.equals(getNext(), that.getNext());
}
@Override
public int hashCode() {
- // You can override field hashCode logic by defining methods like:
- // int fieldNameHashCode() { ... }
-
int _hash = 1;
- _hash = 31 * _hash + mUid;
- _hash = 31 * _hash + Objects.hashCode(mPackageName);
- _hash = 31 * _hash + Objects.hashCode(mAttributionTag);
- _hash = 31 * _hash + Objects.hashCode(mToken);
- _hash = 31 * _hash + Objects.hashCode(mRenouncedPermissions);
- _hash = 31 * _hash + Objects.hashCode(mNext);
+ _hash = 31 * _hash + mAttributionSourceState.uid;
+ _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.packageName);
+ _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.attributionTag);
+ _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.token);
+ _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.renouncedPermissions);
+ _hash = 31 * _hash + Objects.hashCode(getNext());
return _hash;
}
- static Parcelling<Set<String>> sParcellingForRenouncedPermissions =
- Parcelling.Cache.get(
- RenouncedPermissionsParcelling.class);
- static {
- if (sParcellingForRenouncedPermissions == null) {
- sParcellingForRenouncedPermissions = Parcelling.Cache.put(
- new RenouncedPermissionsParcelling());
- }
- }
-
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- // You can override field parcelling by defining methods like:
- // void parcelFieldName(Parcel dest, int flags) { ... }
-
- byte flg = 0;
- if (mPackageName != null) flg |= 0x2;
- if (mAttributionTag != null) flg |= 0x4;
- if (mToken != null) flg |= 0x8;
- if (mRenouncedPermissions != null) flg |= 0x10;
- if (mNext != null) flg |= 0x20;
- dest.writeByte(flg);
- dest.writeInt(mUid);
- if (mPackageName != null) dest.writeString(mPackageName);
- if (mAttributionTag != null) dest.writeString(mAttributionTag);
- if (mToken != null) dest.writeStrongBinder(mToken);
- sParcellingForRenouncedPermissions.parcel(mRenouncedPermissions, dest, flags);
- if (mNext != null) dest.writeTypedObject(mNext, flags);
+ mAttributionSourceState.writeToParcel(dest, flags);
}
@Override
public int describeContents() { return 0; }
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- /* package-private */ AttributionSource(@NonNull Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
- byte flg = in.readByte();
- int uid = in.readInt();
- String packageName = (flg & 0x2) == 0 ? null : in.readString();
- String attributionTag = (flg & 0x4) == 0 ? null : in.readString();
- IBinder token = (flg & 0x8) == 0 ? null : in.readStrongBinder();
- Set<String> renouncedPermissions = sParcellingForRenouncedPermissions.unparcel(in);
- AttributionSource next = (flg & 0x20) == 0 ? null : (AttributionSource) in.readTypedObject(AttributionSource.CREATOR);
-
- this.mUid = uid;
- this.mPackageName = packageName;
- this.mAttributionTag = attributionTag;
- this.mToken = token;
- this.mRenouncedPermissions = renouncedPermissions;
- com.android.internal.util.AnnotationValidations.validate(
- SystemApi.class, null, mRenouncedPermissions);
- com.android.internal.util.AnnotationValidations.validate(
- RequiresPermission.class, null, mRenouncedPermissions,
- "value", android.Manifest.permission.RENOUNCE_PERMISSIONS);
- this.mNext = next;
-
- // onConstructed(); // You can define this method to get a callback
- }
-
public static final @NonNull Parcelable.Creator<AttributionSource> CREATOR
= new Parcelable.Creator<AttributionSource>() {
@Override
@@ -506,15 +378,9 @@ public final class AttributionSource implements Parcelable {
/**
* A builder for {@link AttributionSource}
*/
- @SuppressWarnings("WeakerAccess")
- public static final class Builder extends BaseBuilder {
-
- private int mUid;
- private @Nullable String mPackageName;
- private @Nullable String mAttributionTag;
- private @Nullable IBinder mToken;
- private @Nullable Set<String> mRenouncedPermissions;
- private @Nullable AttributionSource mNext;
+ public static final class Builder {
+ private @NonNull final AttributionSourceState mAttributionSourceState =
+ new AttributionSourceState();
private long mBuilderFieldsSet = 0L;
@@ -524,9 +390,8 @@ public final class AttributionSource implements Parcelable {
* @param uid
* The UID that is accessing the permission protected data.
*/
- public Builder(
- int uid) {
- mUid = uid;
+ public Builder(int uid) {
+ mAttributionSourceState.uid = uid;
}
/**
@@ -535,7 +400,7 @@ public final class AttributionSource implements Parcelable {
public @NonNull Builder setPackageName(@Nullable String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x2;
- mPackageName = value;
+ mAttributionSourceState.packageName = value;
return this;
}
@@ -545,7 +410,7 @@ public final class AttributionSource implements Parcelable {
public @NonNull Builder setAttributionTag(@Nullable String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x4;
- mAttributionTag = value;
+ mAttributionSourceState.attributionTag = value;
return this;
}
@@ -578,7 +443,8 @@ public final class AttributionSource implements Parcelable {
public @NonNull Builder setRenouncedPermissions(@Nullable Set<String> value) {
checkNotUsed();
mBuilderFieldsSet |= 0x10;
- mRenouncedPermissions = value;
+ mAttributionSourceState.renouncedPermissions = (value != null)
+ ? value.toArray(new String[0]) : null;
return this;
}
@@ -588,7 +454,8 @@ public final class AttributionSource implements Parcelable {
public @NonNull Builder setNext(@Nullable AttributionSource value) {
checkNotUsed();
mBuilderFieldsSet |= 0x20;
- mNext = value;
+ mAttributionSourceState.next = (value != null) ? new AttributionSourceState[]
+ {value.mAttributionSourceState} : null;
return this;
}
@@ -598,28 +465,21 @@ public final class AttributionSource implements Parcelable {
mBuilderFieldsSet |= 0x40; // Mark builder used
if ((mBuilderFieldsSet & 0x2) == 0) {
- mPackageName = null;
+ mAttributionSourceState.packageName = null;
}
if ((mBuilderFieldsSet & 0x4) == 0) {
- mAttributionTag = null;
+ mAttributionSourceState.attributionTag = null;
}
if ((mBuilderFieldsSet & 0x8) == 0) {
- mToken = null;
+ mAttributionSourceState.token = null;
}
if ((mBuilderFieldsSet & 0x10) == 0) {
- mRenouncedPermissions = null;
+ mAttributionSourceState.renouncedPermissions = null;
}
if ((mBuilderFieldsSet & 0x20) == 0) {
- mNext = null;
+ mAttributionSourceState.next = null;
}
- AttributionSource o = new AttributionSource(
- mUid,
- mPackageName,
- mAttributionTag,
- mToken,
- mRenouncedPermissions,
- mNext);
- return o;
+ return new AttributionSource(mAttributionSourceState);
}
private void checkNotUsed() {
@@ -629,9 +489,4 @@ public final class AttributionSource implements Parcelable {
}
}
}
-
-
- //@formatter:on
- // End of generated code
-
}
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index 5089f30585b4..66e088359459 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -16,21 +16,19 @@
package android.content;
-import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
-import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
import android.os.Binder;
+import android.os.IBinder;
import android.os.Process;
-import android.util.Slog;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.permission.IPermissionChecker;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
/**
* This class provides permission check APIs that verify both the
@@ -72,34 +70,44 @@ import java.util.concurrent.ConcurrentHashMap;
* @hide
*/
public final class PermissionChecker {
- private static final String LOG_TAG = PermissionChecker.class.getName();
-
- private static final String PLATFORM_PACKAGE_NAME = "android";
-
- /** The permission is granted. */
- public static final int PERMISSION_GRANTED = AppOpsManager.MODE_ALLOWED;
+ /**
+ * The permission is granted.
+ *
+ * @hide
+ */
+ public static final int PERMISSION_GRANTED = IPermissionChecker.PERMISSION_GRANTED;
- /** Only for runtime permissions, its returned when the runtime permission
- * is granted, but the corresponding app op is denied. */
- public static final int PERMISSION_SOFT_DENIED = AppOpsManager.MODE_IGNORED;
+ /**
+ * The permission is denied. Applicable only to runtime and app op permissions.
+ *
+ * <p>Returned when:
+ * <ul>
+ * <li>the runtime permission is granted, but the corresponding app op is denied
+ * for runtime permissions.</li>
+ * <li>the app ops is ignored for app op permissions.</li>
+ * </ul>
+ *
+ * @hide
+ */
+ public static final int PERMISSION_SOFT_DENIED = IPermissionChecker.PERMISSION_SOFT_DENIED;
- /** Returned when:
+ /**
+ * The permission is denied.
+ *
+ * <p>Returned when:
* <ul>
- * <li>For non app op permissions, returned when the permission is denied.</li>
- * <li>For app op permissions, returned when the app op is denied or app op is
- * {@link AppOpsManager#MODE_DEFAULT} and permission is denied.</li>
+ * <li>the permission is denied for non app op permissions.</li>
+ * <li>the app op is denied or app op is {@link AppOpsManager#MODE_DEFAULT}
+ * and permission is denied.</li>
* </ul>
*
+ * @hide
*/
- public static final int PERMISSION_HARD_DENIED = AppOpsManager.MODE_ERRORED;
+ public static final int PERMISSION_HARD_DENIED = IPermissionChecker.PERMISSION_HARD_DENIED;
/** Constant when the PID for which we check permissions is unknown. */
public static final int PID_UNKNOWN = -1;
- // Cache for platform defined runtime permissions to avoid multi lookup (name -> info)
- private static final ConcurrentHashMap<String, PermissionInfo> sPlatformPermissions
- = new ConcurrentHashMap<>();
-
/** @hide */
@IntDef({PERMISSION_GRANTED,
PERMISSION_SOFT_DENIED,
@@ -107,6 +115,8 @@ public final class PermissionChecker {
@Retention(RetentionPolicy.SOURCE)
public @interface PermissionResult {}
+ private static volatile IPermissionChecker sService;
+
private PermissionChecker() {
/* do nothing */
}
@@ -232,7 +242,7 @@ public final class PermissionChecker {
public static int checkPermissionForDataDeliveryFromDataSource(@NonNull Context context,
@NonNull String permission, int pid, @NonNull AttributionSource attributionSource,
@Nullable String message) {
- return checkPermissionForDataDeliveryCommon(context, permission, pid, attributionSource,
+ return checkPermissionForDataDeliveryCommon(context, permission, attributionSource,
message, false /*startDataDelivery*/, /*fromDatasource*/ true);
}
@@ -307,21 +317,23 @@ public final class PermissionChecker {
public static int checkPermissionForDataDelivery(@NonNull Context context,
@NonNull String permission, int pid, @NonNull AttributionSource attributionSource,
@Nullable String message, boolean startDataDelivery) {
- return checkPermissionForDataDeliveryCommon(context, permission, pid, attributionSource,
+ return checkPermissionForDataDeliveryCommon(context, permission, attributionSource,
message, startDataDelivery, /*fromDatasource*/ false);
}
private static int checkPermissionForDataDeliveryCommon(@NonNull Context context,
- @NonNull String permission, int pid, @NonNull AttributionSource attributionSource,
+ @NonNull String permission, @NonNull AttributionSource attributionSource,
@Nullable String message, boolean startDataDelivery, boolean fromDatasource) {
// If the check failed in the middle of the chain, finish any started op.
- final int result = checkPermissionCommon(context, permission, attributionSource,
- message, true /*forDataDelivery*/, startDataDelivery, fromDatasource);
- if (startDataDelivery && result != PERMISSION_GRANTED) {
- finishDataDelivery(context, AppOpsManager.permissionToOp(permission),
- attributionSource);
+ try {
+ final int result = getPermissionCheckerService().checkPermission(permission,
+ attributionSource.asState(), message, true /*forDataDelivery*/,
+ startDataDelivery, fromDatasource);
+ return result;
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
- return result;
+ return PERMISSION_HARD_DENIED;
}
/**
@@ -356,9 +368,14 @@ public final class PermissionChecker {
public static int checkPermissionAndStartDataDelivery(@NonNull Context context,
@NonNull String permission, @NonNull AttributionSource attributionSource,
@Nullable String message) {
- return checkPermissionCommon(context, permission, attributionSource,
- message, true /*forDataDelivery*/, /*startDataDelivery*/ true,
- /*fromDatasource*/ false);
+ try {
+ return getPermissionCheckerService().checkPermission(permission,
+ attributionSource.asState(), message, true /*forDataDelivery*/,
+ /*startDataDelivery*/ true, /*fromDatasource*/ false);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return PERMISSION_HARD_DENIED;
}
/**
@@ -390,13 +407,14 @@ public final class PermissionChecker {
public static int startOpForDataDelivery(@NonNull Context context,
@NonNull String opName, @NonNull AttributionSource attributionSource,
@Nullable String message) {
- final int result = checkOp(context, AppOpsManager.strOpToOp(opName), attributionSource,
- message, true /*forDataDelivery*/, true /*startDataDelivery*/);
- // It is important to finish any started op if some step in the attribution chain failed.
- if (result != PERMISSION_GRANTED) {
- finishDataDelivery(context, opName, attributionSource);
+ try {
+ return getPermissionCheckerService().checkOp(
+ AppOpsManager.strOpToOp(opName), attributionSource.asState(), message,
+ true /*forDataDelivery*/, true /*startDataDelivery*/);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
- return result;
+ return PERMISSION_HARD_DENIED;
}
/**
@@ -412,15 +430,10 @@ public final class PermissionChecker {
*/
public static void finishDataDelivery(@NonNull Context context, @NonNull String op,
@NonNull AttributionSource attributionSource) {
- if (op == null || attributionSource.getPackageName() == null) {
- return;
- }
-
- final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
- appOpsManager.finishProxyOp(op, attributionSource);
-
- if (attributionSource.getNext() != null) {
- finishDataDelivery(context, op, attributionSource.getNext());
+ try {
+ getPermissionCheckerService().finishDataDelivery(op, attributionSource.asState());
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
}
@@ -456,8 +469,14 @@ public final class PermissionChecker {
public static int checkOpForPreflight(@NonNull Context context,
@NonNull String opName, @NonNull AttributionSource attributionSource,
@Nullable String message) {
- return checkOp(context, AppOpsManager.strOpToOp(opName), attributionSource,
- message, false /*forDataDelivery*/, false /*startDataDelivery*/);
+ try {
+ return getPermissionCheckerService().checkOp(AppOpsManager.strOpToOp(opName),
+ attributionSource.asState(), message, false /*forDataDelivery*/,
+ false /*startDataDelivery*/);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return PERMISSION_HARD_DENIED;
}
/**
@@ -489,8 +508,14 @@ public final class PermissionChecker {
public static int checkOpForDataDelivery(@NonNull Context context,
@NonNull String opName, @NonNull AttributionSource attributionSource,
@Nullable String message) {
- return checkOp(context, AppOpsManager.strOpToOp(opName), attributionSource,
- message, true /*forDataDelivery*/, false /*startDataDelivery*/);
+ try {
+ return getPermissionCheckerService().checkOp(AppOpsManager.strOpToOp(opName),
+ attributionSource.asState(), message, true /*forDataDelivery*/,
+ false /*startDataDelivery*/);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return PERMISSION_HARD_DENIED;
}
/**
@@ -561,9 +586,14 @@ public final class PermissionChecker {
@PermissionResult
public static int checkPermissionForPreflight(@NonNull Context context,
@NonNull String permission, @NonNull AttributionSource attributionSource) {
- return checkPermissionCommon(context, permission, attributionSource,
- null /*message*/, false /*forDataDelivery*/, /*startDataDelivery*/ false,
- /*fromDatasource*/ false);
+ try {
+ return getPermissionCheckerService().checkPermission(permission,
+ attributionSource.asState(), null /*message*/, false /*forDataDelivery*/,
+ /*startDataDelivery*/ false, /*fromDatasource*/ false);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return PERMISSION_HARD_DENIED;
}
/**
@@ -798,356 +828,12 @@ public final class PermissionChecker {
Binder.getCallingUid(), packageName);
}
- @PermissionResult
- private static int checkPermissionCommon(@NonNull Context context, @NonNull String permission,
- @NonNull AttributionSource attributionSource,
- @Nullable String message, boolean forDataDelivery, boolean startDataDelivery,
- boolean fromDatasource) {
- PermissionInfo permissionInfo = sPlatformPermissions.get(permission);
-
- if (permissionInfo == null) {
- try {
- permissionInfo = context.getPackageManager().getPermissionInfo(permission, 0);
- if (PLATFORM_PACKAGE_NAME.equals(permissionInfo.packageName)) {
- // Double addition due to concurrency is fine - the backing store is concurrent.
- sPlatformPermissions.put(permission, permissionInfo);
- }
- } catch (PackageManager.NameNotFoundException ignored) {
- return PERMISSION_HARD_DENIED;
- }
- }
-
- if (permissionInfo.isAppOp()) {
- return checkAppOpPermission(context, permission, attributionSource, message,
- forDataDelivery, fromDatasource);
- }
- if (permissionInfo.isRuntime()) {
- return checkRuntimePermission(context, permission, attributionSource, message,
- forDataDelivery, startDataDelivery, fromDatasource);
- }
-
- if (!fromDatasource && !checkPermission(context, permission, attributionSource.getUid(),
- attributionSource.getRenouncedPermissions())) {
- return PERMISSION_HARD_DENIED;
- }
-
- if (attributionSource.getNext() != null) {
- return checkPermissionCommon(context, permission,
- attributionSource.getNext(), message, forDataDelivery,
- startDataDelivery, /*fromDatasource*/ false);
- }
-
- return PERMISSION_GRANTED;
- }
-
- @PermissionResult
- private static int checkAppOpPermission(@NonNull Context context, @NonNull String permission,
- @NonNull AttributionSource attributionSource, @Nullable String message,
- boolean forDataDelivery, boolean fromDatasource) {
- final int op = AppOpsManager.permissionToOpCode(permission);
- if (op < 0) {
- Slog.wtf(LOG_TAG, "Appop permission " + permission + " with no app op defined!");
- return PERMISSION_HARD_DENIED;
- }
-
- AttributionSource current = attributionSource;
- AttributionSource next = null;
-
- while (true) {
- final boolean skipCurrentChecks = (fromDatasource || next != null);
-
- next = current.getNext();
-
- // If the call is from a datasource we need to vet only the chain before it. This
- // way we can avoid the datasource creating an attribution context for every call.
- if (!(fromDatasource && current == attributionSource)
- && next != null && !current.isTrusted(context)) {
- return PERMISSION_HARD_DENIED;
- }
-
- // The access is for oneself if this is the single receiver of data
- // after the data source or if this is the single attribution source
- // in the chain if not from a datasource.
- final boolean singleReceiverFromDatasource = (fromDatasource
- && current == attributionSource && next != null && next.getNext() == null);
- final boolean selfAccess = singleReceiverFromDatasource || next == null;
-
- final int opMode = performOpTransaction(context, op, current, message,
- forDataDelivery, /*startDataDelivery*/ false, skipCurrentChecks,
- selfAccess, singleReceiverFromDatasource);
-
- switch (opMode) {
- case AppOpsManager.MODE_IGNORED:
- case AppOpsManager.MODE_ERRORED: {
- return PERMISSION_HARD_DENIED;
- }
- case AppOpsManager.MODE_DEFAULT: {
- if (!skipCurrentChecks && !checkPermission(context, permission,
- attributionSource.getUid(), attributionSource
- .getRenouncedPermissions())) {
- return PERMISSION_HARD_DENIED;
- }
- if (next != null && !checkPermission(context, permission,
- next.getUid(), next.getRenouncedPermissions())) {
- return PERMISSION_HARD_DENIED;
- }
- }
- }
-
- if (next == null || next.getNext() == null) {
- return PERMISSION_GRANTED;
- }
-
- current = next;
- }
- }
-
- private static int checkRuntimePermission(@NonNull Context context, @NonNull String permission,
- @NonNull AttributionSource attributionSource, @Nullable String message,
- boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource) {
- // Now let's check the identity chain...
- final int op = AppOpsManager.permissionToOpCode(permission);
-
- AttributionSource current = attributionSource;
- AttributionSource next = null;
-
- while (true) {
- final boolean skipCurrentChecks = (fromDatasource || next != null);
- next = current.getNext();
-
- // If the call is from a datasource we need to vet only the chain before it. This
- // way we can avoid the datasource creating an attribution context for every call.
- if (!(fromDatasource && current == attributionSource)
- && next != null && !current.isTrusted(context)) {
- return PERMISSION_HARD_DENIED;
- }
-
- // If we already checked the permission for this one, skip the work
- if (!skipCurrentChecks && !checkPermission(context, permission,
- current.getUid(), current.getRenouncedPermissions())) {
- return PERMISSION_HARD_DENIED;
- }
-
- if (next != null && !checkPermission(context, permission,
- next.getUid(), next.getRenouncedPermissions())) {
- return PERMISSION_HARD_DENIED;
- }
-
- if (op < 0) {
- // Bg location is one-off runtime modifier permission and has no app op
- if (sPlatformPermissions.contains(permission)
- && !Manifest.permission.ACCESS_BACKGROUND_LOCATION.equals(permission)) {
- Slog.wtf(LOG_TAG, "Platform runtime permission " + permission
- + " with no app op defined!");
- }
- if (next == null) {
- return PERMISSION_GRANTED;
- }
- current = next;
- continue;
- }
-
- // The access is for oneself if this is the single receiver of data
- // after the data source or if this is the single attribution source
- // in the chain if not from a datasource.
- final boolean singleReceiverFromDatasource = (fromDatasource
- && current == attributionSource && next != null && next.getNext() == null);
- final boolean selfAccess = singleReceiverFromDatasource || next == null;
-
- final int opMode = performOpTransaction(context, op, current, message,
- forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess,
- singleReceiverFromDatasource);
-
- switch (opMode) {
- case AppOpsManager.MODE_ERRORED: {
- return PERMISSION_HARD_DENIED;
- }
- case AppOpsManager.MODE_IGNORED: {
- return PERMISSION_SOFT_DENIED;
- }
- }
-
- if (next == null || next.getNext() == null) {
- return PERMISSION_GRANTED;
- }
-
- current = next;
- }
- }
-
- private static boolean checkPermission(@NonNull Context context, @NonNull String permission,
- int uid, @NonNull Set<String> renouncedPermissions) {
- final boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1,
- uid) == PackageManager.PERMISSION_GRANTED;
- if (permissionGranted && renouncedPermissions.contains(permission)
- && context.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS,
- /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED) {
- return false;
- }
- return permissionGranted;
- }
-
- private static int checkOp(@NonNull Context context, @NonNull int op,
- @NonNull AttributionSource attributionSource, @Nullable String message,
- boolean forDataDelivery, boolean startDataDelivery) {
- if (op < 0 || attributionSource.getPackageName() == null) {
- return PERMISSION_HARD_DENIED;
- }
-
- AttributionSource current = attributionSource;
- AttributionSource next = null;
-
- while (true) {
- final boolean skipCurrentChecks = (next != null);
- next = current.getNext();
-
- // If the call is from a datasource we need to vet only the chain before it. This
- // way we can avoid the datasource creating an attribution context for every call.
- if (next != null && !current.isTrusted(context)) {
- return PERMISSION_HARD_DENIED;
- }
-
- // The access is for oneself if this is the single attribution source in the chain.
- final boolean selfAccess = (next == null);
-
- final int opMode = performOpTransaction(context, op, current, message,
- forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess,
- /*fromDatasource*/ false);
-
- switch (opMode) {
- case AppOpsManager.MODE_ERRORED: {
- return PERMISSION_HARD_DENIED;
- }
- case AppOpsManager.MODE_IGNORED: {
- return PERMISSION_SOFT_DENIED;
- }
- }
-
- if (next == null || next.getNext() == null) {
- return PERMISSION_GRANTED;
- }
-
- current = next;
- }
- }
-
- private static int performOpTransaction(@NonNull Context context, int op,
- @NonNull AttributionSource attributionSource, @Nullable String message,
- boolean forDataDelivery, boolean startDataDelivery, boolean skipProxyOperation,
- boolean selfAccess, boolean singleReceiverFromDatasource) {
- // We cannot perform app ops transactions without a package name. In all relevant
- // places we pass the package name but just in case there is a bug somewhere we
- // do a best effort to resolve the package from the UID (pick first without a loss
- // of generality - they are in the same security sandbox).
- final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
- final AttributionSource accessorSource = (!singleReceiverFromDatasource)
- ? attributionSource : attributionSource.getNext();
- if (!forDataDelivery) {
- final String resolvedAccessorPackageName = resolvePackageName(context, accessorSource);
- if (resolvedAccessorPackageName == null) {
- return AppOpsManager.MODE_ERRORED;
- }
- final int opMode = appOpsManager.unsafeCheckOpRawNoThrow(op,
- accessorSource.getUid(), resolvedAccessorPackageName);
- final AttributionSource next = accessorSource.getNext();
- if (!selfAccess && opMode == AppOpsManager.MODE_ALLOWED && next != null) {
- final String resolvedNextPackageName = resolvePackageName(context, next);
- if (resolvedNextPackageName == null) {
- return AppOpsManager.MODE_ERRORED;
- }
- return appOpsManager.unsafeCheckOpRawNoThrow(op, next.getUid(),
- resolvedNextPackageName);
- }
- return opMode;
- } else if (startDataDelivery) {
- final AttributionSource resolvedAttributionSource = resolveAttributionSource(
- context, accessorSource);
- if (resolvedAttributionSource.getPackageName() == null) {
- return AppOpsManager.MODE_ERRORED;
- }
- if (selfAccess) {
- // If the datasource is not in a trusted platform component then in would not
- // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that
- // an app is exposing runtime permission protected data but cannot blame others
- // in a trusted way which would not properly show in permission usage UIs.
- // As a fallback we note a proxy op that blames the app and the datasource.
- try {
- return appOpsManager.startOpNoThrow(op, resolvedAttributionSource.getUid(),
- resolvedAttributionSource.getPackageName(),
- /*startIfModeDefault*/ false,
- resolvedAttributionSource.getAttributionTag(),
- message);
- } catch (SecurityException e) {
- Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with"
- + " platform defined runtime permission "
- + AppOpsManager.opToPermission(op) + " while not having "
- + Manifest.permission.UPDATE_APP_OPS_STATS);
- return appOpsManager.startProxyOpNoThrow(op, attributionSource, message,
- skipProxyOperation);
- }
- } else {
- return appOpsManager.startProxyOpNoThrow(op, resolvedAttributionSource, message,
- skipProxyOperation);
- }
- } else {
- final AttributionSource resolvedAttributionSource = resolveAttributionSource(
- context, accessorSource);
- if (resolvedAttributionSource.getPackageName() == null) {
- return AppOpsManager.MODE_ERRORED;
- }
- if (selfAccess) {
- // If the datasource is not in a trusted platform component then in would not
- // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that
- // an app is exposing runtime permission protected data but cannot blame others
- // in a trusted way which would not properly show in permission usage UIs.
- // As a fallback we note a proxy op that blames the app and the datasource.
- try {
- return appOpsManager.noteOpNoThrow(op, resolvedAttributionSource.getUid(),
- resolvedAttributionSource.getPackageName(),
- resolvedAttributionSource.getAttributionTag(),
- message);
- } catch (SecurityException e) {
- Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with"
- + " platform defined runtime permission "
- + AppOpsManager.opToPermission(op) + " while not having "
- + Manifest.permission.UPDATE_APP_OPS_STATS);
- return appOpsManager.noteProxyOpNoThrow(op, attributionSource, message,
- skipProxyOperation);
- }
- } else {
- return appOpsManager.noteProxyOpNoThrow(op, resolvedAttributionSource, message,
- skipProxyOperation);
- }
- }
- }
-
- private static @Nullable String resolvePackageName(@NonNull Context context,
- @NonNull AttributionSource attributionSource) {
- if (attributionSource.getPackageName() != null) {
- return attributionSource.getPackageName();
- }
- final String[] packageNames = context.getPackageManager().getPackagesForUid(
- attributionSource.getUid());
- if (packageNames != null) {
- // This is best effort if the caller doesn't pass a package. The security
- // sandbox is UID, therefore we pick an arbitrary package.
- return packageNames[0];
- }
- // Last resort to handle special UIDs like root, etc.
- return AppOpsManager.resolvePackageName(attributionSource.getUid(),
- attributionSource.getPackageName());
- }
-
- private static @NonNull AttributionSource resolveAttributionSource(
- @NonNull Context context, @NonNull AttributionSource attributionSource) {
- if (attributionSource.getPackageName() != null) {
- return attributionSource;
+ private static @NonNull IPermissionChecker getPermissionCheckerService() {
+ // Race is fine, we may end up looking up the same instance twice, no big deal.
+ if (sService == null) {
+ final IBinder service = ServiceManager.getService("permission_checker");
+ sService = IPermissionChecker.Stub.asInterface(service);
}
- return new AttributionSource(attributionSource.getUid(),
- resolvePackageName(context, attributionSource),
- attributionSource.getAttributionTag(),
- attributionSource.getToken(),
- attributionSource.getRenouncedPermissions(),
- attributionSource.getNext());
+ return sService;
}
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 725576fda389..22d75ef93137 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -3069,13 +3069,6 @@ public class ParsingPackageUtils {
/**
* @hide
*/
- public static void setCompatibilityModeEnabled(boolean compatibilityModeEnabled) {
- sCompatibilityModeEnabled = compatibilityModeEnabled;
- }
-
- /**
- * @hide
- */
public static void readConfigUseRoundIcon(Resources r) {
if (r != null) {
sUseRoundIcon = r.getBoolean(com.android.internal.R.bool.config_useRoundIcon);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 51d196dc8292..17116e248646 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -223,14 +223,6 @@ public final class InputManager {
public static final long BLOCK_FLAG_SLIPPERY = android.os.IInputConstants.BLOCK_FLAG_SLIPPERY;
/**
- * Check whether apps are using MotionEvent.getRawX/Y. This is implementation-specific, and
- * thus undefined for most 3p app usages.
- * @hide
- */
- @ChangeId
- public static final long APP_USES_RAW_INPUT_COORDS = 179274888L;
-
- /**
* Input Event Injection Synchronization Mode: None.
* Never blocks. Injection is asynchronous and is assumed always to be successful.
* @hide
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index d026e959905c..2a9b703583a6 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -560,9 +560,6 @@ public final class BinderProxy implements IBinder {
}
}
- final AppOpsManager.PausedNotedAppOpsCollection prevCollection =
- AppOpsManager.pauseNotedAppOpsCollection();
-
if ((flags & FLAG_ONEWAY) == 0 && AppOpsManager.isListeningForOpNoted()) {
flags |= FLAG_COLLECT_NOTED_APP_OPS;
}
@@ -571,8 +568,6 @@ public final class BinderProxy implements IBinder {
boolean replyOwnsNative = (reply == null) ? false : reply.ownsNativeParcelObject();
return transactNative(code, data, reply, replyOwnsNative, flags);
} finally {
- AppOpsManager.resumeNotedAppOpsCollection(prevCollection);
-
if (transactListener != null) {
transactListener.onTransactEnded(session);
}
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index b2c1e7a908e7..820f53164704 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -837,8 +837,8 @@ public final class PrintAttributes implements Parcelable {
new MediaSize("JPN_YOU4", "android",
R.string.mediasize_japanese_you4, 4134, 9252);
/** Japanese Photo L media size: 89mm x 127mm (3.5 x 5") */
- public static final @NonNull MediaSize OE_PHOTO_L =
- new MediaSize("OE_PHOTO_L", "android",
+ public static final @NonNull MediaSize JPN_OE_PHOTO_L =
+ new MediaSize("JPN_OE_PHOTO_L", "android",
R.string.mediasize_japanese_l, 3500, 5000);
private final @NonNull String mId;
diff --git a/core/java/android/util/JsonReader.java b/core/java/android/util/JsonReader.java
index 50f63f8db304..c75e23807432 100644
--- a/core/java/android/util/JsonReader.java
+++ b/core/java/android/util/JsonReader.java
@@ -16,7 +16,7 @@
package android.util;
-import libcore.internal.StringPool;
+import com.android.internal.util.StringPool;
import java.io.Closeable;
import java.io.EOFException;
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 31ca8e124f9e..6801c27851a9 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -16,7 +16,6 @@
package android.view;
-import static android.hardware.input.InputManager.APP_USES_RAW_INPUT_COORDS;
import static android.view.Display.DEFAULT_DISPLAY;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -24,7 +23,6 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.TestApi;
-import android.compat.Compatibility;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Matrix;
import android.os.Build;
@@ -2674,7 +2672,6 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* @see #AXIS_X
*/
public final float getRawX() {
- Compatibility.reportUnconditionalChange(APP_USES_RAW_INPUT_COORDS);
return nativeGetRawAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
}
@@ -2688,7 +2685,6 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* @see #AXIS_Y
*/
public final float getRawY() {
- Compatibility.reportUnconditionalChange(APP_USES_RAW_INPUT_COORDS);
return nativeGetRawAxisValue(mNativePtr, AXIS_Y, 0, HISTORY_CURRENT);
}
@@ -2705,7 +2701,6 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* @see #AXIS_X
*/
public float getRawX(int pointerIndex) {
- Compatibility.reportUnconditionalChange(APP_USES_RAW_INPUT_COORDS);
return nativeGetRawAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT);
}
@@ -2722,7 +2717,6 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* @see #AXIS_Y
*/
public float getRawY(int pointerIndex) {
- Compatibility.reportUnconditionalChange(APP_USES_RAW_INPUT_COORDS);
return nativeGetRawAxisValue(mNativePtr, AXIS_Y, pointerIndex, HISTORY_CURRENT);
}
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 933760c8bf18..8debe5d569a3 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -70,6 +70,7 @@ public final class SplashScreenView extends FrameLayout {
private static final boolean DEBUG = false;
private boolean mNotCopyable;
+ private boolean mRevealAnimationSupported = true;
private int mInitBackgroundColor;
private int mInitIconBackgroundColor;
private View mIconView;
@@ -264,6 +265,25 @@ public final class SplashScreenView extends FrameLayout {
}
/**
+ * If set to true, indicates to the system that this view can be dismissed by playing the
+ * Reveal animation.
+ * <p>
+ * If the exit animation is handled by the client, the animation won't be played anyway.
+ * @hide
+ */
+ public void setRevealAnimationSupported(boolean support) {
+ mRevealAnimationSupported = support;
+ }
+
+ /**
+ * Whether this view support reveal animation.
+ * @hide
+ */
+ public boolean isRevealAnimationSupported() {
+ return mRevealAnimationSupported;
+ }
+
+ /**
* Returns the duration of the icon animation if icon is animatable.
*
* @see android.R.attr#windowSplashScreenAnimatedIcon
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 2b4e09d66851..83def0cc1fd6 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -80,6 +80,8 @@ public class AccessibilityShortcutController {
"com.android.server.accessibility.MagnificationController";
public static final ComponentName MAGNIFICATION_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "Magnification");
+ public static final ComponentName ONE_HANDED_COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "OneHandedMode");
public static final ComponentName REDUCE_BRIGHT_COLORS_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "ReduceBrightColors");
@@ -117,7 +119,7 @@ public class AccessibilityShortcutController {
public static Map<ComponentName, ToggleableFrameworkFeatureInfo>
getFrameworkShortcutFeaturesMap() {
if (sFrameworkShortcutFeaturesMap == null) {
- Map<ComponentName, ToggleableFrameworkFeatureInfo> featuresMap = new ArrayMap<>(2);
+ Map<ComponentName, ToggleableFrameworkFeatureInfo> featuresMap = new ArrayMap<>(4);
featuresMap.put(COLOR_INVERSION_COMPONENT_NAME,
new ToggleableFrameworkFeatureInfo(
Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
@@ -128,6 +130,11 @@ public class AccessibilityShortcutController {
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
"1" /* Value to enable */, "0" /* Value to disable */,
R.string.color_correction_feature_name));
+ featuresMap.put(ONE_HANDED_COMPONENT_NAME,
+ new ToggleableFrameworkFeatureInfo(
+ Settings.Secure.ONE_HANDED_MODE_ACTIVATED,
+ "1" /* Value to enable */, "0" /* Value to disable */,
+ R.string.one_handed_mode_feature_name));
featuresMap.put(REDUCE_BRIGHT_COLORS_COMPONENT_NAME,
new ToggleableFrameworkFeatureInfo(
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index f2d91ba30842..4a20ad0f33f5 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -21,6 +21,7 @@ import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTT
import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME;
import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType;
import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained;
@@ -230,6 +231,16 @@ public final class AccessibilityTargetHelper {
context.getDrawable(R.drawable.ic_accessibility_color_inversion),
Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+ final ToggleAllowListingFeatureTarget oneHandedMode =
+ new ToggleAllowListingFeatureTarget(context,
+ shortcutType,
+ isShortcutContained(context, shortcutType,
+ ONE_HANDED_COMPONENT_NAME.flattenToString()),
+ ONE_HANDED_COMPONENT_NAME.flattenToString(),
+ context.getString(R.string.one_handed_mode_feature_name),
+ context.getDrawable(R.drawable.ic_accessibility_one_handed),
+ Settings.Secure.ONE_HANDED_MODE_ACTIVATED);
+
final ToggleAllowListingFeatureTarget reduceBrightColors =
new ToggleAllowListingFeatureTarget(context,
shortcutType,
@@ -243,6 +254,7 @@ public final class AccessibilityTargetHelper {
targets.add(magnification);
targets.add(daltonizer);
targets.add(colorInversion);
+ targets.add(oneHandedMode);
targets.add(reduceBrightColors);
return targets;
diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java
index 2785c21baf95..bce0d6076d24 100644
--- a/core/java/com/android/internal/app/procstats/DumpUtils.java
+++ b/core/java/com/android/internal/app/procstats/DumpUtils.java
@@ -93,7 +93,6 @@ public final class DumpUtils {
STATE_LABELS[STATE_BOUND_TOP_OR_FGS] = "Bnd TopFgs";
STATE_LABELS[STATE_FGS] = " Fgs";
STATE_LABELS[STATE_IMPORTANT_FOREGROUND] = " Imp Fg";
- STATE_LABELS[STATE_IMPORTANT_FOREGROUND] = " Imp Fg";
STATE_LABELS[STATE_IMPORTANT_BACKGROUND] = " Imp Bg";
STATE_LABELS[STATE_BACKUP] = " Backup";
STATE_LABELS[STATE_SERVICE] = " Service";
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index 4bced27cafb4..d259b7fe0782 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -1550,6 +1550,10 @@ public final class ProcessState {
final int key = mDurations.getKeyAt(i);
final int type = SparseMappingTable.getIdFromKey(key);
final int aggregatedType = DumpUtils.aggregateCurrentProcessState(type);
+ if ((type % STATE_COUNT) == STATE_SERVICE_RESTARTING) {
+ // Skip restarting service state -- that is not actually a running process.
+ continue;
+ }
long time = mDurations.getValue(key);
if (mCurCombinedState == type) {
@@ -1563,7 +1567,9 @@ public final class ProcessState {
durationByState.put(aggregatedType, time);
}
}
- if (!didCurState && mCurCombinedState != STATE_NOTHING) {
+ if (!didCurState && mCurCombinedState != STATE_NOTHING
+ && (mCurCombinedState % STATE_COUNT) != STATE_SERVICE_RESTARTING) {
+ // Skip restarting service state -- that is not actually a running process.
final int aggregatedType = DumpUtils.aggregateCurrentProcessState(mCurCombinedState);
int index = durationByState.indexOfKey(aggregatedType);
if (index >= 0) {
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
index 36bc22906695..182dba71d0d7 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
@@ -39,14 +39,14 @@ public final class CompatibilityChangeConfig implements Parcelable {
* Changes forced to be enabled.
*/
public Set<Long> enabledChanges() {
- return mChangeConfig.forceEnabledSet();
+ return mChangeConfig.getEnabledSet();
}
/**
* Changes forced to be disabled.
*/
public Set<Long> disabledChanges() {
- return mChangeConfig.forceDisabledSet();
+ return mChangeConfig.getDisabledSet();
}
/**
@@ -84,8 +84,8 @@ public final class CompatibilityChangeConfig implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
- long[] enabled = mChangeConfig.forceEnabledChangesArray();
- long[] disabled = mChangeConfig.forceDisabledChangesArray();
+ long[] enabled = mChangeConfig.getEnabledChangesArray();
+ long[] disabled = mChangeConfig.getDisabledChangesArray();
dest.writeLongArray(enabled);
dest.writeLongArray(disabled);
diff --git a/core/java/com/android/internal/os/BinderDeathDispatcher.java b/core/java/com/android/internal/os/BinderDeathDispatcher.java
index 0c93f7f160e4..8ca6241e63c6 100644
--- a/core/java/com/android/internal/os/BinderDeathDispatcher.java
+++ b/core/java/com/android/internal/os/BinderDeathDispatcher.java
@@ -24,12 +24,11 @@ import android.os.IInterface;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import java.io.PrintWriter;
-
/**
* Multiplexes multiple binder death recipients on the same binder objects, so that at the native
* level, we only need to keep track of one death recipient reference. This will help reduce the
@@ -63,6 +62,10 @@ public class BinderDeathDispatcher<T extends IInterface> {
@Override
public void binderDied() {
+ }
+
+ @Override
+ public void binderDied(IBinder who) {
final ArraySet<DeathRecipient> copy;
synchronized (mLock) {
copy = mRecipients;
@@ -77,7 +80,7 @@ public class BinderDeathDispatcher<T extends IInterface> {
// Let's call it without holding the lock.
final int size = copy.size();
for (int i = 0; i < size; i++) {
- copy.valueAt(i).binderDied();
+ copy.valueAt(i).binderDied(who);
}
}
}
@@ -124,13 +127,12 @@ public class BinderDeathDispatcher<T extends IInterface> {
}
}
- public void dump(PrintWriter pw, String indent) {
+ /** Dump stats useful for debugging. Can be used from dump() methods of client services. */
+ public void dump(IndentingPrintWriter pw) {
synchronized (mLock) {
- pw.print(indent);
pw.print("# of watched binders: ");
pw.println(mTargets.size());
- pw.print(indent);
pw.print("# of death recipients: ");
int n = 0;
for (RecipientsInfo info : mTargets.values()) {
diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING
index 14cdb0890b25..2b22f08652e7 100644
--- a/core/java/com/android/internal/os/TEST_MAPPING
+++ b/core/java/com/android/internal/os/TEST_MAPPING
@@ -13,6 +13,16 @@
]
},
{
+ "file_patterns": [
+ "BinderDeathDispatcher\\.java"
+ ],
+ "name": "FrameworksCoreTests",
+ "options": [
+ { "include-filter": "com.android.internal.os.BinderDeathDispatcherTest" },
+ { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
+ ]
+ },
+ {
"file_patterns": ["Battery[^/]*\\.java"],
"name": "FrameworksServicesTests",
"options": [
diff --git a/core/java/com/android/internal/util/StringPool.java b/core/java/com/android/internal/util/StringPool.java
new file mode 100644
index 000000000000..c5180a3fe8cf
--- /dev/null
+++ b/core/java/com/android/internal/util/StringPool.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 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.internal.util;
+
+/**
+ * A pool of string instances. Unlike the {@link String#intern() VM's
+ * interned strings}, this pool provides no guarantee of reference equality.
+ * It is intended only to save allocations. This class is not thread safe.
+ *
+ * @hide
+ */
+public final class StringPool {
+
+ private final String[] mPool = new String[512];
+
+ /**
+ * Constructs string pool.
+ */
+ public StringPool() {
+ }
+
+ private static boolean contentEquals(String s, char[] chars, int start, int length) {
+ if (s.length() != length) {
+ return false;
+ }
+ for (int i = 0; i < length; i++) {
+ if (chars[start + i] != s.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns a string equal to {@code new String(array, start, length)}.
+ *
+ * @param array buffer containing string chars
+ * @param start offset in {@code array} where string starts
+ * @param length length of string
+ * @return string equal to {@code new String(array, start, length)}
+ */
+ public String get(char[] array, int start, int length) {
+ // Compute an arbitrary hash of the content
+ int hashCode = 0;
+ for (int i = start; i < start + length; i++) {
+ hashCode = (hashCode * 31) + array[i];
+ }
+
+ // Pick a bucket using Doug Lea's supplemental secondaryHash function (from HashMap)
+ hashCode ^= (hashCode >>> 20) ^ (hashCode >>> 12);
+ hashCode ^= (hashCode >>> 7) ^ (hashCode >>> 4);
+ int index = hashCode & (mPool.length - 1);
+
+ String pooled = mPool[index];
+ if (pooled != null && contentEquals(pooled, array, start, length)) {
+ return pooled;
+ }
+
+ String result = new String(array, start, length);
+ mPool[index] = result;
+ return result;
+ }
+}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index de4cede6b7fe..6755be13c933 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -638,6 +638,7 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p
char hotstartupsamplesOptsBuf[sizeof("-Xps-hot-startup-method-samples:")-1 + PROPERTY_VALUE_MAX];
char saveResolvedClassesDelayMsOptsBuf[
sizeof("-Xps-save-resolved-classes-delay-ms:")-1 + PROPERTY_VALUE_MAX];
+ char profileMinSavePeriodOptsBuf[sizeof("-Xps-min-save-period-ms:")-1 + PROPERTY_VALUE_MAX];
char madviseRandomOptsBuf[sizeof("-XX:MadviseRandomAccess:")-1 + PROPERTY_VALUE_MAX];
char madviseWillNeedFileSizeVdex[
sizeof("-XMadviseWillNeedVdexFileSize:")-1 + PROPERTY_VALUE_MAX];
@@ -867,6 +868,9 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p
parseRuntimeOption("dalvik.vm.ps-resolved-classes-delay-ms", saveResolvedClassesDelayMsOptsBuf,
"-Xps-save-resolved-classes-delay-ms:");
+ parseRuntimeOption("dalvik.vm.ps-min-save-period-ms", profileMinSavePeriodOptsBuf,
+ "-Xps-min-save-period-ms:");
+
property_get("ro.config.low_ram", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
addOption("-XX:LowMemoryMode");
diff --git a/core/res/res/drawable/ic_accessibility_one_handed.xml b/core/res/res/drawable/ic_accessibility_one_handed.xml
new file mode 100644
index 000000000000..0a469ba731ee
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_one_handed.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+<background android:drawable="@color/accessibility_feature_background" />
+<foreground>
+ <inset
+ android:drawable="@drawable/ic_accessibility_one_handed_foreground"
+ android:inset="@dimen/accessibility_icon_foreground_padding_ratio" />
+</foreground>
+</adaptive-icon> \ No newline at end of file
diff --git a/core/res/res/drawable/ic_accessibility_one_handed_foreground.xml b/core/res/res/drawable/ic_accessibility_one_handed_foreground.xml
new file mode 100644
index 000000000000..18e56187efb3
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_one_handed_foreground.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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:width="18dp"
+ android:height="18dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path android:fillColor="@android:color/white"
+ android:pathData="M4.64169 3C3.88567 3 3.27271 3.61296 3.27271 4.36898V18.4011C3.27271 19.154 3.88875 19.7701 4.64169 19.7701H12.5339C12.5339 19.7701 12.5425 18.0588 11.2324 18.0588H6.01067C5.44597 18.0588 4.98393 17.5968 4.98393 17.0321V5.73797C4.98393 5.17326 5.44597 4.71123 6.01067 4.71123H15.9358C16.5005 4.71123 16.9625 5.17326 16.9625 5.73797V9.84492C16.9625 10.9651 16.4899 12.5952 15.5936 13.2674L9.77538 9.16043C8.58505 10.425 8.88177 11.705 10.1176 12.9251L13.1978 16.0053C13.1978 16.0053 13.3231 17.4572 13.5401 18.0588C14.2034 19.8984 16.2781 20.7968 16.2781 20.7968H19.231H19.2967C20.0835 20.7968 20.7273 20.153 20.7273 19.3662V12.2543L18.9441 4.06781C18.7662 3.23718 18.4068 3 17.8729 3H4.64169Z"/>
+</vector> \ No newline at end of file
diff --git a/core/res/res/values-es-rMX/donottranslate-cldr.xml b/core/res/res/values-es-rMX/donottranslate-cldr.xml
new file mode 100755
index 000000000000..db438f22208f
--- /dev/null
+++ b/core/res/res/values-es-rMX/donottranslate-cldr.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="month_day_year">%-e %B %Y</string>
+ <string name="time_of_day">%H:%M:%S</string>
+ <string name="date_and_time">%-e %b %Y, %H:%M:%S</string>
+ <string name="date_time">%1$s, %2$s</string>
+</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a0c6f451f14e..e86d2ce617b7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4636,6 +4636,9 @@
shown in the warning dialog about the accessibility shortcut. -->
<string name="color_correction_feature_name">Color Correction</string>
+ <!-- Title of One Handed Mode feature, shown in the system gestures of the accessibility shortcut. [CHAR LIMIT=none] -->
+ <string name="one_handed_mode_feature_name">One-Handed mode</string>
+
<!-- Title of Reduce Brightness feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
<string name="reduce_bright_colors_feature_name">Extra dim</string>
@@ -5007,10 +5010,10 @@
<string name="confirm_battery_saver">OK</string>
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description, with a "learn more" link. -->
- <string name="battery_saver_description_with_learn_more">Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, and certain features.\n\n<annotation id="url">Learn more</annotation></string>
+ <string name="battery_saver_description_with_learn_more">Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, certain features, and some network connections.\n\n<annotation id="url">Learn more</annotation></string>
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description, without a "learn more" link. -->
- <string name="battery_saver_description">Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, and certain features.</string>
+ <string name="battery_saver_description">Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, certain features, and some network connections.</string>
<!-- [CHAR_LIMIT=NONE] Data saver: Feature description -->
<string name="data_saver_description">To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5019ccadd353..3a22efce70fc 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3410,9 +3410,12 @@
<java-symbol type="drawable" name="ic_accessibility_color_correction" />
<java-symbol type="drawable" name="ic_accessibility_magnification" />
<java-symbol type="drawable" name="ic_accessibility_reduce_bright_colors" />
+ <java-symbol type="drawable" name="ic_accessibility_one_handed" />
<java-symbol type="string" name="reduce_bright_colors_feature_name" />
+ <java-symbol type="string" name="one_handed_mode_feature_name" />
+
<!-- com.android.internal.widget.RecyclerView -->
<java-symbol type="id" name="item_touch_helper_previous_elevation"/>
<java-symbol type="dimen" name="item_touch_helper_max_drag_scroll_per_frame"/>
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
index 942045c8bf35..83103333f68b 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -17,6 +17,7 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
@@ -31,14 +32,14 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.FileDescriptor;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BinderDeathDispatcherTest {
@@ -120,7 +121,7 @@ public class BinderDeathDispatcherTest {
public void die() {
isAlive = false;
if (mRecipient != null) {
- mRecipient.binderDied();
+ mRecipient.binderDied(this);
}
mRecipient = null;
}
@@ -227,33 +228,33 @@ public class BinderDeathDispatcherTest {
// Kill the targets.
t1.die();
- verify(r1, times(1)).binderDied();
- verify(r2, times(1)).binderDied();
- verify(r3, times(1)).binderDied();
- verify(r4, times(0)).binderDied();
- verify(r5, times(0)).binderDied();
+ verify(r1, times(1)).binderDied(t1);
+ verify(r2, times(1)).binderDied(t1);
+ verify(r3, times(1)).binderDied(t1);
+ verify(r4, times(0)).binderDied(any());
+ verify(r5, times(0)).binderDied(any());
assertThat(d.getTargetsForTest().size()).isEqualTo(2);
reset(r1, r2, r3, r4, r5);
t2.die();
- verify(r1, times(1)).binderDied();
- verify(r2, times(0)).binderDied();
- verify(r3, times(0)).binderDied();
- verify(r4, times(0)).binderDied();
- verify(r5, times(0)).binderDied();
+ verify(r1, times(1)).binderDied(t2);
+ verify(r2, times(0)).binderDied(any());
+ verify(r3, times(0)).binderDied(any());
+ verify(r4, times(0)).binderDied(any());
+ verify(r5, times(0)).binderDied(any());
assertThat(d.getTargetsForTest().size()).isEqualTo(1);
reset(r1, r2, r3, r4, r5);
t3.die();
- verify(r1, times(0)).binderDied();
- verify(r2, times(0)).binderDied();
- verify(r3, times(1)).binderDied();
- verify(r4, times(0)).binderDied();
- verify(r5, times(1)).binderDied();
+ verify(r1, times(0)).binderDied(any());
+ verify(r2, times(0)).binderDied(any());
+ verify(r3, times(1)).binderDied(t3);
+ verify(r4, times(0)).binderDied(any());
+ verify(r5, times(1)).binderDied(t3);
assertThat(d.getTargetsForTest().size()).isEqualTo(0);
@@ -262,4 +263,27 @@ public class BinderDeathDispatcherTest {
assertThat(d.getTargetsForTest().size()).isEqualTo(0);
}
+
+ @Test
+ public void duplicateRegistrations() {
+ BinderDeathDispatcher<MyTarget> d = new BinderDeathDispatcher<>();
+
+ MyTarget t1 = new MyTarget();
+
+ DeathRecipient r1 = mock(DeathRecipient.class);
+ DeathRecipient r2 = mock(DeathRecipient.class);
+
+ for (int i = 0; i < 5; i++) {
+ assertThat(d.linkToDeath(t1, r1)).isEqualTo(1);
+ }
+ assertThat(d.linkToDeath(t1, r2)).isEqualTo(2);
+
+ t1.die();
+ verify(r1, times(1)).binderDied(t1);
+ verify(r2, times(1)).binderDied(t1);
+
+ d.unlinkToDeath(t1, r1);
+ d.unlinkToDeath(t1, r2);
+ assertThat(d.getTargetsForTest()).isEmpty();
+ }
}
diff --git a/core/tests/utiltests/src/com/android/internal/util/StringPoolTest.java b/core/tests/utiltests/src/com/android/internal/util/StringPoolTest.java
new file mode 100644
index 000000000000..f67fd516fcf6
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/StringPoolTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 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.internal.util;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+@SmallTest
+public final class StringPoolTest extends AndroidTestCase {
+
+ public void testStringPool() {
+ StringPool stringPool = new StringPool();
+ String bcd = stringPool.get(new char[] { 'a', 'b', 'c', 'd', 'e' }, 1, 3);
+ assertEquals("bcd", bcd);
+ assertSame(bcd, stringPool.get(new char[] { 'a', 'b', 'c', 'd', 'e' }, 1, 3));
+ }
+
+ public void testHashCollision() {
+ StringPool stringPool = new StringPool();
+ char[] a = { (char) 1, (char) 0 };
+ char[] b = { (char) 0, (char) 31 };
+ assertEquals(new String(a).hashCode(), new String(b).hashCode());
+
+ String aString = stringPool.get(a, 0, 2);
+ assertEquals(new String(a), aString);
+ String bString = stringPool.get(b, 0, 2);
+ assertEquals(new String(b), bString);
+ assertSame(bString, stringPool.get(b, 0, 2));
+ assertNotSame(aString, stringPool.get(a, 0, 2));
+ }
+}
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index cff7dccac296..a03931bfab24 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -369,7 +369,9 @@ public final class Icon implements Parcelable {
final PackageManager pm = context.getPackageManager();
try {
ApplicationInfo ai = pm.getApplicationInfo(
- resPackage, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+ resPackage,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.GET_SHARED_LIBRARY_FILES);
if (ai != null) {
mObj1 = pm.getResourcesForApplication(ai);
} else {
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index e8757b5d96f4..edf000b5e68a 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -52,9 +52,6 @@
when the PIP menu is shown in center. -->
<string translatable="false" name="pip_menu_bounds">"596 280 1324 690"</string>
- <!-- Animation duration when exit starting window: icon going away -->
- <integer name="starting_window_icon_exit_anim_duration">166</integer>
-
<!-- Animation duration when exit starting window: reveal app -->
<integer name="starting_window_app_reveal_anim_duration">333</integer>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index ae7ab528ed0a..ce57638f16f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -135,6 +135,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
}
};
+ private final ContentObserver mActivatedObserver;
private final ContentObserver mEnabledObserver;
private final ContentObserver mTimeoutObserver;
private final ContentObserver mTaskChangeExitObserver;
@@ -170,11 +171,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
@Override
public void onStartFinished(Rect bounds) {
mState.setState(STATE_ACTIVE);
+ notifyShortcutState(STATE_ACTIVE);
}
@Override
public void onStopFinished(Rect bounds) {
mState.setState(STATE_NONE);
+ notifyShortcutState(STATE_NONE);
}
};
@@ -287,6 +290,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
context.getContentResolver(), mUserId);
mTimeoutHandler = timeoutHandler;
+ mActivatedObserver = getObserver(this::onActivatedActionChanged);
mEnabledObserver = getObserver(this::onEnabledSettingChanged);
mTimeoutObserver = getObserver(this::onTimeoutSettingChanged);
mTaskChangeExitObserver = getObserver(this::onTaskChangeExitSettingChanged);
@@ -348,6 +352,12 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
}
@VisibleForTesting
+ void notifyShortcutState(@OneHandedState.State int state) {
+ mOneHandedSettingsUtil.setOneHandedModeActivated(
+ mContext.getContentResolver(), state == STATE_ACTIVE ? 1 : 0, mUserId);
+ }
+
+ @VisibleForTesting
void startOneHanded() {
if (isLockedDisabled()) {
Slog.d(TAG, "Temporary lock disabled");
@@ -416,6 +426,9 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
}
private void registerSettingObservers(int newUserId) {
+ mOneHandedSettingsUtil.registerSettingsKeyObserver(
+ Settings.Secure.ONE_HANDED_MODE_ACTIVATED,
+ mContext.getContentResolver(), mActivatedObserver, newUserId);
mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED,
mContext.getContentResolver(), mEnabledObserver, newUserId);
mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
@@ -466,6 +479,26 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
}
@VisibleForTesting
+ void onActivatedActionChanged() {
+ if (mState.isTransitioning() || !isOneHandedEnabled()) {
+ return;
+ }
+
+ final boolean isActivated = mState.getState() == STATE_ACTIVE;
+ final boolean requestActivated = mOneHandedSettingsUtil.getOneHandedModeActivated(
+ mContext.getContentResolver(), mUserId);
+ // When gesture trigger action, we will update settings and introduce observer callback
+ // again, then the following logic will just ignore the second redundant callback.
+ if (isActivated ^ requestActivated) {
+ if (requestActivated) {
+ startOneHanded();
+ } else {
+ stopOneHanded();
+ }
+ }
+ }
+
+ @VisibleForTesting
void onEnabledSettingChanged() {
final boolean enabled = mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
mContext.getContentResolver(), mUserId);
@@ -544,6 +577,11 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
return mLockedDisabled;
}
+ @VisibleForTesting
+ boolean isOneHandedEnabled() {
+ return mIsOneHandedEnabled;
+ }
+
private void updateOneHandedEnabled() {
if (mState.getState() == STATE_ENTERING || mState.getState() == STATE_ACTIVE) {
mMainExecutor.execute(() -> stopOneHanded());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
index 2ab51f3f1313..bb68224b0a87 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
@@ -131,8 +131,28 @@ public final class OneHandedSettingsUtil {
* Returns whether swipe bottom to notification gesture enabled or not.
*/
public boolean getSettingsSwipeToNotificationEnabled(ContentResolver resolver, int userId) {
- return Settings.Secure.getInt(resolver,
- Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0 /* Default OFF */) == 1;
+ return Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0, userId) == 1;
+ }
+
+ /**
+ * Sets one handed activated or not to notify state for shortcut
+ *
+ * @return activated or not
+ */
+ public boolean getOneHandedModeActivated(ContentResolver resolver, int userId) {
+ return Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ONE_HANDED_MODE_ACTIVATED, 0, userId) == 1;
+ }
+
+ /**
+ * Sets one handed activated or not to notify state for shortcut
+ *
+ * @return activated or not
+ */
+ public boolean setOneHandedModeActivated(ContentResolver resolver, int state, int userId) {
+ return Settings.Secure.putIntForUser(resolver,
+ Settings.Secure.ONE_HANDED_MODE_ACTIVATED, state, userId);
}
void dump(PrintWriter pw, String prefix, ContentResolver resolver,
@@ -145,6 +165,8 @@ public final class OneHandedSettingsUtil {
pw.println(getSettingsOneHandedModeTimeout(resolver, userId));
pw.print(innerPrefix + "tapsAppToExit=");
pw.println(getSettingsTapsAppToExit(resolver, userId));
+ pw.print(innerPrefix + "shortcutActivated=");
+ pw.println(getOneHandedModeActivated(resolver, userId));
}
public OneHandedSettingsUtil() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
index 5bc2afd11fe8..3fe57c61c0bb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.startingsurface;
+import static android.view.Choreographer.CALLBACK_COMMIT;
import static android.view.View.GONE;
import android.animation.Animator;
@@ -29,13 +30,12 @@ import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.Shader;
import android.util.Slog;
+import android.view.Choreographer;
import android.view.SurfaceControl;
+import android.view.SyncRtSurfaceTransactionApplier;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.view.animation.Transformation;
@@ -53,51 +53,36 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
private static final String TAG = StartingSurfaceDrawer.TAG;
- private static final Interpolator ICON_EXIT_INTERPOLATOR = new PathInterpolator(1f, 0f, 1f, 1f);
private static final Interpolator APP_EXIT_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f);
- private static final int EXTRA_REVEAL_DELAY = 133;
private final Matrix mTmpTransform = new Matrix();
- private final float[] mTmpFloat9 = new float[9];
- private SurfaceControl mFirstWindowSurface;
+ private final SurfaceControl mFirstWindowSurface;
private final Rect mFirstWindowFrame = new Rect();
private final SplashScreenView mSplashScreenView;
private final int mMainWindowShiftLength;
- private final int mIconShiftLength;
private final int mAppDuration;
- private final int mIconDuration;
private final TransactionPool mTransactionPool;
private ValueAnimator mMainAnimator;
- private Animation mShiftUpAnimation;
- private AnimationSet mIconAnimationSet;
+ private ShiftUpAnimation mShiftUpAnimation;
private Runnable mFinishCallback;
SplashScreenExitAnimation(SplashScreenView view, SurfaceControl leash, Rect frame,
- int appDuration, int iconDuration, int mainWindowShiftLength, int iconShiftLength,
- TransactionPool pool, Runnable handleFinish) {
+ int appDuration, int mainWindowShiftLength, TransactionPool pool,
+ Runnable handleFinish) {
mSplashScreenView = view;
mFirstWindowSurface = leash;
if (frame != null) {
mFirstWindowFrame.set(frame);
}
mAppDuration = appDuration;
- mIconDuration = iconDuration;
mMainWindowShiftLength = mainWindowShiftLength;
- mIconShiftLength = iconShiftLength;
mFinishCallback = handleFinish;
mTransactionPool = pool;
}
- void prepareAnimations() {
- prepareRevealAnimation();
- prepareShiftAnimation();
- }
-
void startAnimations() {
- if (mIconAnimationSet != null) {
- mIconAnimationSet.start();
- }
+ prepareRevealAnimation();
if (mMainAnimator != null) {
mMainAnimator.start();
}
@@ -114,8 +99,7 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
mMainAnimator.setInterpolator(APP_EXIT_INTERPOLATOR);
mMainAnimator.addListener(this);
- final int startDelay = mIconDuration + EXTRA_REVEAL_DELAY;
- final float transparentRatio = 0.95f;
+ final float transparentRatio = 0.8f;
final int globalHeight = mSplashScreenView.getHeight();
final int verticalCircleCenter = 0;
final int finalVerticalLength = globalHeight - verticalCircleCenter;
@@ -130,9 +114,8 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
final float[] stops = {0f, transparentRatio, 1f};
radialVanishAnimation.setRadialPaintParam(colors, stops);
radialVanishAnimation.setReady();
- mMainAnimator.setStartDelay(startDelay);
- if (mFirstWindowSurface != null) {
+ if (mFirstWindowSurface != null && mFirstWindowSurface.isValid()) {
// shift up main window
View occludeHoleView = new View(mSplashScreenView.getContext());
if (DEBUG_EXIT_ANIMATION_BLEND) {
@@ -144,59 +127,16 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
WindowManager.LayoutParams.MATCH_PARENT, mMainWindowShiftLength);
mSplashScreenView.addView(occludeHoleView, params);
- mShiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength);
+ mShiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength, occludeHoleView);
mShiftUpAnimation.setDuration(mAppDuration);
mShiftUpAnimation.setInterpolator(APP_EXIT_INTERPOLATOR);
- mShiftUpAnimation.setStartOffset(startDelay);
occludeHoleView.setAnimation(mShiftUpAnimation);
}
}
- // shift down icon and branding view
- private void prepareShiftAnimation() {
- final View iconView = mSplashScreenView.getIconView();
- if (iconView == null) {
- return;
- }
- if (mIconShiftLength > 0) {
- mIconAnimationSet = new AnimationSet(true /* shareInterpolator */);
- if (DEBUG_EXIT_ANIMATION) {
- Slog.v(TAG, "first exit animation, shift length: " + mIconShiftLength);
- }
- mIconAnimationSet.addAnimation(new TranslateYAnimation(0, mIconShiftLength));
- mIconAnimationSet.addAnimation(new AlphaAnimation(1, 0));
- mIconAnimationSet.setAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {
-
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- if (DEBUG_EXIT_ANIMATION) {
- Slog.v(TAG, "first exit animation finished");
- }
- iconView.post(() -> iconView.setVisibility(GONE));
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
- // ignore
- }
- });
- mIconAnimationSet.setDuration(mIconDuration);
- mIconAnimationSet.setInterpolator(ICON_EXIT_INTERPOLATOR);
- iconView.setAnimation(mIconAnimationSet);
- final View brandingView = mSplashScreenView.getBrandingView();
- if (brandingView != null) {
- brandingView.setAnimation(mIconAnimationSet);
- }
- }
- }
-
private static class RadialVanishAnimation extends View {
- private SplashScreenView mView;
+ private final SplashScreenView mView;
private int mInitRadius;
private int mFinishRadius;
private boolean mReady;
@@ -217,7 +157,7 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
mVanishMatrix.setScale(scale, scale);
mVanishMatrix.postTranslate(mCircleCenter.x, mCircleCenter.y);
mVanishPaint.getShader().setLocalMatrix(mVanishMatrix);
- mView.postInvalidate();
+ postInvalidate();
});
mView.addView(this);
}
@@ -262,28 +202,57 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
}
private final class ShiftUpAnimation extends TranslateYAnimation {
- ShiftUpAnimation(float fromYDelta, float toYDelta) {
+ final SyncRtSurfaceTransactionApplier mApplier;
+ ShiftUpAnimation(float fromYDelta, float toYDelta, View targetView) {
super(fromYDelta, toYDelta);
+ mApplier = new SyncRtSurfaceTransactionApplier(targetView);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
- if (mFirstWindowSurface == null) {
+ if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) {
return;
}
mTmpTransform.set(t.getMatrix());
+
+ // set the vsyncId to ensure the transaction doesn't get applied too early.
final SurfaceControl.Transaction tx = mTransactionPool.acquire();
+ tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
mTmpTransform.postTranslate(mFirstWindowFrame.left,
mFirstWindowFrame.top + mMainWindowShiftLength);
- tx.setMatrix(mFirstWindowSurface, mTmpTransform, mTmpFloat9);
- // TODO set the vsyncId to ensure the transaction doesn't get applied too early.
- // Additionally, do you want to have this synchronized with your view animations?
- // If so, you'll need to use SyncRtSurfaceTransactionApplier
- tx.apply();
+
+ SyncRtSurfaceTransactionApplier.SurfaceParams
+ params = new SyncRtSurfaceTransactionApplier.SurfaceParams
+ .Builder(mFirstWindowSurface)
+ .withMatrix(mTmpTransform)
+ .withMergeTransaction(tx)
+ .build();
+ mApplier.scheduleApply(params);
+
mTransactionPool.release(tx);
}
+
+ void finish() {
+ if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) {
+ return;
+ }
+ final SurfaceControl.Transaction tx = mTransactionPool.acquire();
+ tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+
+ SyncRtSurfaceTransactionApplier.SurfaceParams
+ params = new SyncRtSurfaceTransactionApplier.SurfaceParams
+ .Builder(mFirstWindowSurface)
+ .withWindowCrop(null)
+ .withMergeTransaction(tx)
+ .build();
+ mApplier.scheduleApply(params);
+ mTransactionPool.release(tx);
+
+ Choreographer.getSfInstance().postCallback(CALLBACK_COMMIT,
+ mFirstWindowSurface::release, null);
+ }
}
private void reset() {
@@ -297,12 +266,8 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener {
mFinishCallback = null;
}
});
- if (mFirstWindowSurface != null) {
- final SurfaceControl.Transaction tx = mTransactionPool.acquire();
- tx.setWindowCrop(mFirstWindowSurface, null);
- tx.apply();
- mFirstWindowSurface.release();
- mFirstWindowSurface = null;
+ if (mShiftUpAnimation != null) {
+ mShiftUpAnimation.finish();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 7a33f50bab1d..e656f43cfe35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -75,20 +75,15 @@ public class SplashscreenContentDrawer {
private int mBrandingImageWidth;
private int mBrandingImageHeight;
private final int mAppRevealDuration;
- private final int mIconExitDuration;
private int mMainWindowShiftLength;
- private int mIconNormalExitDistance;
- private int mIconEarlyExitDistance;
private final TransactionPool mTransactionPool;
private final SplashScreenWindowAttrs mTmpAttrs = new SplashScreenWindowAttrs();
private final Handler mSplashscreenWorkerHandler;
- SplashscreenContentDrawer(Context context, int iconExitAnimDuration, int appRevealAnimDuration,
- TransactionPool pool) {
+ SplashscreenContentDrawer(Context context, int appRevealAnimDuration, TransactionPool pool) {
mContext = context;
mIconProvider = new IconProvider(context);
mAppRevealDuration = appRevealAnimDuration;
- mIconExitDuration = iconExitAnimDuration;
mTransactionPool = pool;
// Initialize Splashscreen worker thread
@@ -139,10 +134,6 @@ public class SplashscreenContentDrawer {
com.android.wm.shell.R.dimen.starting_surface_brand_image_height);
mMainWindowShiftLength = mContext.getResources().getDimensionPixelSize(
com.android.wm.shell.R.dimen.starting_surface_exit_animation_window_shift_length);
- mIconNormalExitDistance = mContext.getResources().getDimensionPixelSize(
- com.android.wm.shell.R.dimen.starting_surface_normal_exit_icon_distance);
- mIconEarlyExitDistance = mContext.getResources().getDimensionPixelSize(
- com.android.wm.shell.R.dimen.starting_surface_early_exit_icon_distance);
}
private int getSystemBGColor() {
@@ -245,7 +236,7 @@ public class SplashscreenContentDrawer {
private SplashScreenView mCachedResult;
private int mThemeColor;
private Drawable mFinalIconDrawable;
- private float mScale = 1f;
+ private int mFinalIconSize = mIconSize;
StartingWindowViewBuilder setWindowBGColor(@ColorInt int background) {
mThemeColor = background;
@@ -282,48 +273,46 @@ public class SplashscreenContentDrawer {
Drawable iconDrawable;
final int animationDuration;
- final int iconSize;
if (mEmptyView) {
// empty splash screen case
animationDuration = 0;
- iconSize = 0;
+ mFinalIconSize = 0;
} else if (mTmpAttrs.mReplaceIcon != null) {
// replaced icon, don't process
iconDrawable = mTmpAttrs.mReplaceIcon;
animationDuration = mTmpAttrs.mAnimationDuration;
- createIconDrawable(iconDrawable, mIconSize);
- iconSize = mIconSize;
+ createIconDrawable(iconDrawable);
} else {
- final float iconScale = (float) mIconSize / (float) mDefaultIconSize;
- iconDrawable = mIconProvider.getIcon(mActivityInfo);
+ final float iconScale = (float) mIconSize / (float) mDefaultIconSize;
+ final int densityDpi = mContext.getResources().getDisplayMetrics().densityDpi;
+ final int scaledIconDpi = (int) (0.5f + iconScale * densityDpi);
+ iconDrawable = mIconProvider.getIcon(mActivityInfo, scaledIconDpi);
if (iconDrawable == null) {
iconDrawable = mContext.getPackageManager().getDefaultActivityIcon();
}
- if (!processAdaptiveIcon(iconDrawable, iconScale)) {
+ if (!processAdaptiveIcon(iconDrawable)) {
if (DEBUG) {
Slog.d(TAG, "The icon is not an AdaptiveIconDrawable");
}
// TODO process legacy icon(bitmap)
- final Drawable tempIcon = loadIconByDensity(iconDrawable, iconScale);
- createIconDrawable(tempIcon, mIconSize);
+ createIconDrawable(iconDrawable);
}
animationDuration = 0;
- iconSize = (int) (0.5 + mIconSize * mScale);
}
- mCachedResult = fillViewWithIcon(iconSize, mFinalIconDrawable, animationDuration);
+ mCachedResult = fillViewWithIcon(mFinalIconSize, mFinalIconDrawable, animationDuration);
mBuildComplete = true;
return mCachedResult;
}
- private void createIconDrawable(Drawable iconDrawable, int iconSize) {
+ private void createIconDrawable(Drawable iconDrawable) {
mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable(
mTmpAttrs.mIconBgColor != Color.TRANSPARENT
? mTmpAttrs.mIconBgColor : mThemeColor,
- iconDrawable, iconSize, mSplashscreenWorkerHandler);
+ iconDrawable, mFinalIconSize, mSplashscreenWorkerHandler);
}
- private boolean processAdaptiveIcon(Drawable iconDrawable, float iconScale) {
+ private boolean processAdaptiveIcon(Drawable iconDrawable) {
if (!(iconDrawable instanceof AdaptiveIconDrawable)) {
return false;
}
@@ -334,7 +323,7 @@ public class SplashscreenContentDrawer {
final DrawableColorTester backIconTester =
new DrawableColorTester(adaptiveIconDrawable.getBackground());
- Drawable iconForeground = adaptiveIconDrawable.getForeground();
+ final Drawable iconForeground = adaptiveIconDrawable.getForeground();
final DrawableColorTester foreIconTester =
new DrawableColorTester(iconForeground, true /* filterTransparent */);
@@ -373,35 +362,20 @@ public class SplashscreenContentDrawer {
final float noBgScale =
foreIconTester.nonTransparentRatio() < ENLARGE_FOREGROUND_ICON_THRESHOLD
? NO_BACKGROUND_SCALE : 1f;
- final Drawable tempIcon = loadIconByDensity(iconDrawable, iconScale * noBgScale);
- if (tempIcon instanceof AdaptiveIconDrawable) {
- mScale = noBgScale;
- iconForeground = ((AdaptiveIconDrawable) tempIcon).getForeground();
- }
// Using AdaptiveIconDrawable here can help keep the shape consistent with the
// current settings.
- final int iconSize = (int) (0.5f + mIconSize * mScale);
- createIconDrawable(iconForeground, iconSize);
+ mFinalIconSize = (int) (0.5f + mIconSize * noBgScale);
+ createIconDrawable(iconForeground);
} else {
if (DEBUG) {
Slog.d(TAG, "makeSplashScreenContentView: draw whole icon");
}
- final Drawable tempIcon = loadIconByDensity(iconDrawable, iconScale);
- createIconDrawable(tempIcon, mIconSize);
+ createIconDrawable(iconDrawable);
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return true;
}
- private Drawable loadIconByDensity(Drawable baseDrawable, float scale) {
- if (scale == 1 && baseDrawable != null) {
- return baseDrawable;
- }
- final int densityDpi = mContext.getResources().getDisplayMetrics().densityDpi;
- final int updateDpi = (int) (0.5f + scale * densityDpi);
- return mIconProvider.getIcon(mActivityInfo, updateDpi);
- }
-
private SplashScreenView fillViewWithIcon(int iconSize, Drawable iconDrawable,
int animationDuration) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "fillViewWithIcon");
@@ -423,6 +397,7 @@ public class SplashscreenContentDrawer {
}
if (mEmptyView) {
splashScreenView.setNotCopyable();
+ splashScreenView.setRevealAnimationSupported(false);
}
splashScreenView.makeSystemUIColorsTransparent();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -695,12 +670,10 @@ public class SplashscreenContentDrawer {
* Create and play the default exit animation for splash screen view.
*/
void applyExitAnimation(SplashScreenView view, SurfaceControl leash,
- Rect frame, boolean isEarlyExit, Runnable finishCallback) {
+ Rect frame, Runnable finishCallback) {
final SplashScreenExitAnimation animation = new SplashScreenExitAnimation(view, leash,
- frame, mAppRevealDuration, mIconExitDuration, mMainWindowShiftLength,
- isEarlyExit ? mIconEarlyExitDistance : mIconNormalExitDistance, mTransactionPool,
+ frame, mAppRevealDuration, mMainWindowShiftLength, mTransactionPool,
finishCallback);
- animation.prepareAnimations();
animation.startAnimations();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 4273f8926bd1..b7a0339aa93d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -35,7 +35,7 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
-import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseArray;
@@ -106,6 +106,8 @@ public class StartingSurfaceDrawer {
private final SplashscreenContentDrawer mSplashscreenContentDrawer;
private Choreographer mChoreographer;
+ private static final boolean DEBUG_ENABLE_REVEAL_ANIMATION =
+ SystemProperties.getBoolean("persist.debug.enable_reveal_animation", false);
/**
* @param splashScreenExecutor The thread used to control add and remove starting window.
*/
@@ -114,12 +116,10 @@ public class StartingSurfaceDrawer {
mContext = context;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mSplashScreenExecutor = splashScreenExecutor;
- final int iconExitAnimDuration = context.getResources().getInteger(
- com.android.wm.shell.R.integer.starting_window_icon_exit_anim_duration);
final int appRevealAnimDuration = context.getResources().getInteger(
com.android.wm.shell.R.integer.starting_window_app_reveal_anim_duration);
- mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, iconExitAnimDuration,
- appRevealAnimDuration, pool);
+ mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, appRevealAnimDuration,
+ pool);
mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance());
}
@@ -467,20 +467,24 @@ public class StartingSurfaceDrawer {
if (DEBUG_SPLASH_SCREEN) {
Slog.v(TAG, "Removing splash screen window for task: " + taskId);
}
- if (record.mContentView != null) {
- if (leash != null || playRevealAnimation) {
- mSplashscreenContentDrawer.applyExitAnimation(record.mContentView,
- leash, frame, record.isEarlyExit(),
- () -> removeWindowInner(record.mDecorView, true));
+ if (record.mContentView != null
+ && record.mContentView.isRevealAnimationSupported()) {
+ if (playRevealAnimation) {
+ if (DEBUG_ENABLE_REVEAL_ANIMATION) {
+ mSplashscreenContentDrawer.applyExitAnimation(record.mContentView,
+ leash, frame,
+ () -> removeWindowInner(record.mDecorView, true));
+ } else {
+ // using the default exit animation from framework
+ removeWindowInner(record.mDecorView, false);
+ }
} else {
- // TODO(183004107) Always hide decorView when playRevealAnimation is enabled
- // from TaskOrganizerController#removeStartingWindow
- // the SplashScreenView has been copied to client, skip default exit
- // animation
- removeWindowInner(record.mDecorView, false);
+ // the SplashScreenView has been copied to client, hide the view to skip
+ // default exit animation
+ removeWindowInner(record.mDecorView, true);
}
} else {
- // no animation will be applied
+ // this is a blank splash screen, don't apply reveal animation
removeWindowInner(record.mDecorView, false);
}
}
@@ -514,12 +518,10 @@ public class StartingSurfaceDrawer {
* Record the view or surface for a starting window.
*/
private static class StartingWindowRecord {
- private static final long EARLY_START_MINIMUM_TIME_MS = 250;
private final View mDecorView;
private final TaskSnapshotWindow mTaskSnapshotWindow;
private SplashScreenView mContentView;
private boolean mSetSplashScreen;
- private long mContentCreateTime;
StartingWindowRecord(View decorView, TaskSnapshotWindow taskSnapshotWindow) {
mDecorView = decorView;
@@ -531,12 +533,7 @@ public class StartingSurfaceDrawer {
return;
}
mContentView = splashScreenView;
- mContentCreateTime = SystemClock.uptimeMillis();
mSetSplashScreen = true;
}
-
- boolean isEarlyExit() {
- return SystemClock.uptimeMillis() - mContentCreateTime < EARLY_START_MINIMUM_TIME_MS;
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index c1282c9d7b12..99342f02463a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -16,7 +16,9 @@
package com.android.wm.shell.onehanded;
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING;
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_EXITING;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
import static com.google.common.truth.Truth.assertThat;
@@ -360,4 +362,71 @@ public class OneHandedControllerTest extends OneHandedTestCase {
verify(mMockGestureHandler).onGestureEnabled(isOneHandedEnabled);
}
+
+ @Test
+ public void testStateActive_shortcutRequestActivate_skipActions() {
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_ACTIVE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mSpiedOneHandedController, never()).startOneHanded();
+ verify(mSpiedOneHandedController, never()).stopOneHanded();
+ }
+
+ @Test
+ public void testStateNotActive_shortcutRequestInActivate_skipAction() {
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(false);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mSpiedOneHandedController, never()).startOneHanded();
+ verify(mSpiedOneHandedController, never()).stopOneHanded();
+ }
+
+ @Test
+ public void testStateNotActive_shortcutRequestActivate_doAction() {
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mSpiedOneHandedController).startOneHanded();
+ verify(mSpiedOneHandedController, never()).stopOneHanded();
+ }
+
+ @Test
+ public void testEnteringTransition_shortcutRequestActivate_skipActions() {
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_ENTERING);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(true);
+ when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mSpiedOneHandedController, never()).startOneHanded();
+ verify(mSpiedOneHandedController, never()).stopOneHanded();
+ }
+
+ @Test
+ public void testExitingTransition_shortcutRequestActivate_skipActions() {
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_EXITING);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(true);
+ when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mSpiedOneHandedController, never()).startOneHanded();
+ verify(mSpiedOneHandedController, never()).stopOneHanded();
+ }
+
+ @Test
+ public void testOneHandedDisabled_shortcutEnabled_skipActions() {
+ when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false);
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mSpiedOneHandedController, never()).startOneHanded();
+ verify(mSpiedOneHandedController, never()).stopOneHanded();
+ }
}
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 32413dc6e841..6403aab08adc 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -36,13 +36,20 @@ import java.util.concurrent.atomic.AtomicReference;
/**
* The SoundPool class manages and plays audio resources for applications.
*
- * <p>A SoundPool is a collection of samples that can be loaded into memory
+ * <p>A SoundPool is a collection of sound samples that can be loaded into memory
* from a resource inside the APK or from a file in the file system. The
- * SoundPool library uses the MediaPlayer service to decode the audio
- * into a raw 16-bit PCM mono or stereo stream. This allows applications
+ * SoundPool library uses the MediaCodec service to decode the audio
+ * into raw 16-bit PCM. This allows applications
* to ship with compressed streams without having to suffer the CPU load
* and latency of decompressing during playback.</p>
*
+ * <p>Soundpool sounds are expected to be short as they are
+ * predecoded into memory. Each decoded sound is internally limited to one
+ * megabyte storage, which represents approximately 5.6 seconds at 44.1kHz stereo
+ * (the duration is proportionally longer at lower sample rates or
+ * a channel mask of mono). A decoded audio sound will be truncated if it would
+ * exceed the per-sound one megabyte storage space.</p>
+ *
* <p>In addition to low-latency playback, SoundPool can also manage the number
* of audio streams being rendered at once. When the SoundPool object is
* constructed, the maxStreams parameter sets the maximum number of streams
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index b0c8c612036a..a0f3098ad347 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -506,7 +506,7 @@ int AImageDecoder_getFrameInfo(AImageDecoder* decoder,
}
int64_t AImageDecoderFrameInfo_getDuration(const AImageDecoderFrameInfo* info) {
- if (!info) return 0;
+ if (!info) return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
return toFrameInfo(info)->fDuration * 1'000'000;
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 95f180a9ba32..0210079646c9 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -23,6 +23,7 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.StringRes;
+import android.app.Activity;
import android.app.AlertDialog;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -632,6 +633,12 @@ public class PackageInstallerActivity extends AlertActivity {
.setPositiveButton(R.string.ok, (dialog, which) -> getActivity().finish())
.create();
}
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ getActivity().setResult(Activity.RESULT_CANCELED);
+ getActivity().finish();
+ }
}
/**
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
index d8b49ab786af..7d9b4d78ce2a 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
@@ -43,7 +43,7 @@
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
app:scrimAnimationDuration="50"
app:scrimVisibleHeightTrigger="@dimen/scrim_visible_height_trigger"
- app:statusBarScrim="@empty"
+ app:statusBarScrim="@null"
app:titleCollapseMode="fade"
app:collapsedTitleTextAppearance="@style/CollapsingToolbarTitle.Collapsed"
app:expandedTitleTextAppearance="@style/CollapsingToolbarTitle.Expanded"
diff --git a/packages/SystemUI/docs/QS-QQS.png b/packages/SystemUI/docs/QS-QQS.png
index 02de479cb8c0..3ed8e24c6a8d 100644
--- a/packages/SystemUI/docs/QS-QQS.png
+++ b/packages/SystemUI/docs/QS-QQS.png
Binary files differ
diff --git a/packages/SystemUI/docs/qs-tiles.md b/packages/SystemUI/docs/qs-tiles.md
index b48ba6708313..521620935ddc 100644
--- a/packages/SystemUI/docs/qs-tiles.md
+++ b/packages/SystemUI/docs/qs-tiles.md
@@ -8,7 +8,7 @@ This document is a more or less comprehensive summary of the state and infrastru
## What are Quick Settings Tiles?
-Quick Settings (from now on, QS) is the expanded panel that contains shortcuts for the user to toggle many settings. This is opened by expanding the notification drawer twice (or once when phone is locked). Quick Quick Settings (QQS) is the smaller panel that appears on top of the notifications before expanding twice and contains some of the toggles with no text.
+Quick Settings (from now on, QS) is the expanded panel that contains shortcuts for the user to toggle many settings. This is opened by expanding the notification drawer twice (or once when phone is locked). Quick Quick Settings (QQS) is the smaller panel that appears on top of the notifications before expanding twice and contains some of the toggles with no secondary line.
Each of these toggles that appear either in QS or QQS are called Quick Settings Tiles (or tiles for short). They allow the user to enable or disable settings quickly and sometimes provides access to more comprehensive settings pages.
@@ -69,13 +69,13 @@ For more information on how to implement a tile in SystemUI, see [Implementing a
Each Tile has a couple of associated views for displaying it in QS and QQS. These views are updated after the backend updates the `State` using `QSTileImpl#handleUpdateState`.
-* **[`com.android.systemui.plugins.qs.QSTileView`](/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java)**: Abstract class that provides basic Tile functionality. These allows external [Factories](#qsfactory) to create Tiles.
-* **[`QSTileBaseView`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java)**: Implementation of `QSTileView` used in QQS that takes care of most of the features of the view:
+* **[`QSTileView`](/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java)**: Abstract class that provides basic Tile functionality. These allows external [Factories](#qsfactory) to create Tiles.
+* **[`QSTileViewImpl`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.java)**: Implementation of `QSTileView`. It takes care of the following:
* Holding the icon
* Background color and shape
* Ripple
* Click listening
-* **[`QSTileView`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java)**: Extends `QSTileBaseView`to add label support. Used in QS.
+ * Labels
* **[`QSIconView`](/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java)**
* **[`QSIconViewImpl`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java)**
@@ -103,7 +103,7 @@ When a container for tiles (`QuickQSPanel` or `QSPanel`) has to display tiles, t
This is a brief run-down of what happens when a user clicks on a tile. Internal changes on the device (for example, changes from Settings) will trigger this process starting in step 3. Throughout this section, we assume that we are dealing with a `QSTileImpl`.
1. User clicks on tile. The following calls happen in sequence:
- 1. `QSTileBaseView#onClickListener`.
+ 1. `QSTileViewImpl#onClickListener`.
2. `QSTile#click`.
3. `QSTileImpl#handleClick`. This last call sets the new state for the device by using the associated controller.
2. State in the device changes. This is normally outside of SystemUI's control.
@@ -113,9 +113,9 @@ This is a brief run-down of what happens when a user clicks on a tile. Internal
4. `QSTileImpl#handleUpdateState` is called to update the state with the new information. This information can be obtained both from the `Object` passed to `refreshState` as well as from the controller.
5. If the state has changed (in at least one element), `QSTileImpl#handleStateChanged` is called. This will trigger a call to all the associated `QSTile.Callback#onStateChanged`, passing the new `State`.
6. `QSTileView#onStateChanged` is called and this calls `QSTileView#handleStateChanged`. This method maps the state into the view:
- * The tile is rippled and the color changes to match the new state.
+ * The tile colors change to match the new state.
* `QSIconView.setIcon` is called to apply the correct state to the icon and the correct icon to the view.
- * If the tile is a `QSTileView` (in expanded QS), the labels are changed.
+ * The tile labels change to match the new state.
## Third party tiles (TileService)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index d1383eb79d85..55dea3d3bcb9 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -163,7 +163,7 @@ public interface QSTile {
public SlashState slash;
public boolean handlesLongClick = true;
public boolean showRippleEffect = true;
- public Drawable sideViewDrawable;
+ public Drawable sideViewCustomDrawable;
public boolean copyTo(State other) {
if (other == null) throw new IllegalArgumentException();
@@ -185,7 +185,7 @@ public interface QSTile {
|| !Objects.equals(other.slash, slash)
|| !Objects.equals(other.handlesLongClick, handlesLongClick)
|| !Objects.equals(other.showRippleEffect, showRippleEffect)
- || !Objects.equals(other.sideViewDrawable, sideViewDrawable);
+ || !Objects.equals(other.sideViewCustomDrawable, sideViewCustomDrawable);
other.icon = icon;
other.iconSupplier = iconSupplier;
other.label = label;
@@ -201,7 +201,7 @@ public interface QSTile {
other.slash = slash != null ? slash.copy() : null;
other.handlesLongClick = handlesLongClick;
other.showRippleEffect = showRippleEffect;
- other.sideViewDrawable = sideViewDrawable;
+ other.sideViewCustomDrawable = sideViewCustomDrawable;
return changed;
}
@@ -227,7 +227,7 @@ public interface QSTile {
sb.append(",isTransient=").append(isTransient);
sb.append(",state=").append(state);
sb.append(",slash=\"").append(slash).append("\"");
- sb.append(",sideViewDrawable").append(sideViewDrawable);
+ sb.append(",sideViewCustomDrawable=").append(sideViewCustomDrawable);
return sb.append(']');
}
@@ -242,12 +242,16 @@ public interface QSTile {
public static class BooleanState extends State {
public static final int VERSION = 1;
public boolean value;
+ public boolean forceExpandIcon;
@Override
public boolean copyTo(State other) {
final BooleanState o = (BooleanState) other;
- final boolean changed = super.copyTo(other) || o.value != value;
+ final boolean changed = super.copyTo(other)
+ || o.value != value
+ || o.forceExpandIcon != forceExpandIcon;
o.value = value;
+ o.forceExpandIcon = forceExpandIcon;
return changed;
}
@@ -255,6 +259,7 @@ public interface QSTile {
protected StringBuilder toStringBuilder() {
final StringBuilder rt = super.toStringBuilder();
rt.insert(rt.length() - 1, ",value=" + value);
+ rt.insert(rt.length() - 1, ",forceExpandIcon=" + forceExpandIcon);
return rt;
}
diff --git a/packages/SystemUI/res-keyguard/layout/qs_media_divider.xml b/packages/SystemUI/res-keyguard/layout/qs_media_divider.xml
deleted file mode 100644
index 1be489cdc700..000000000000
--- a/packages/SystemUI/res-keyguard/layout/qs_media_divider.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<View xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:layout_marginBottom="16dp"
- android:background="@color/media_divider">
-</View> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_call_chip.xml
index a5e7f5d4cfe6..a146547d0083 100644
--- a/packages/SystemUI/res/layout/ongoing_call_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_call_chip.xml
@@ -29,18 +29,17 @@
android:src="@*android:drawable/ic_phone"
android:layout_width="@dimen/ongoing_call_chip_icon_size"
android:layout_height="@dimen/ongoing_call_chip_icon_size"
- android:paddingEnd="@dimen/ongoing_call_chip_icon_text_padding"
android:tint="?android:attr/colorPrimary"
/>
<!-- TODO(b/183229367): The text in this view isn't quite centered within the chip. -->
- <!-- TODO(b/183229367): This text view's width shouldn't change as the time increases. -->
- <Chronometer
+ <com.android.systemui.statusbar.phone.ongoingcall.OngoingCallChronometer
android:id="@+id/ongoing_call_chip_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
- android:gravity="center"
+ android:gravity="center|start"
+ android:paddingStart="@dimen/ongoing_call_chip_icon_text_padding"
android:textAppearance="@android:style/TextAppearance.Material.Small"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:textColor="?android:attr/colorPrimary"
diff --git a/packages/SystemUI/res/layout/qs_carrier_group.xml b/packages/SystemUI/res/layout/qs_carrier_group.xml
index fd53a8bb1a7c..810c9592e422 100644
--- a/packages/SystemUI/res/layout/qs_carrier_group.xml
+++ b/packages/SystemUI/res/layout/qs_carrier_group.xml
@@ -32,11 +32,10 @@
android:minWidth="48dp"
android:minHeight="48dp"
android:gravity="center_vertical"
- android:textAppearance="@style/TextAppearance.QS.Status"
+ android:textAppearance="@style/TextAppearance.QS.Status.NoCarrierText"
android:textDirection="locale"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
- android:maxEms="7"
android:visibility="gone"/>
<include
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 93a47154d927..266ecefd0840 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -22,6 +22,7 @@
android:layout_height="@dimen/qs_footer_height"
android:layout_marginStart="@dimen/qs_footer_margin"
android:layout_marginEnd="@dimen/qs_footer_margin"
+ android:layout_marginBottom="@dimen/qs_footers_margin_bottom"
android:background="@android:color/transparent"
android:baselineAligned="false"
android:clickable="false"
@@ -50,7 +51,6 @@
android:focusable="true"
android:gravity="center_vertical"
android:singleLine="true"
- android:textColor="?android:attr/textColorSecondary"
android:textAppearance="@style/TextAppearance.QS.Build"
android:visibility="gone" />
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 7cf3d014aa8a..4607e5f5cd79 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -42,8 +42,6 @@
android:clipToPadding="false"
android:clipChildren="false">
<include layout="@layout/qs_footer_impl" />
- <include layout="@layout/qs_media_divider"
- android:id="@+id/divider"/>
</com.android.systemui.qs.QSPanel>
</com.android.systemui.qs.NonInterceptingScrollView>
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index f8812ba894fa..1d93f5d69da8 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2016 The Android Open Source Project
+ Copyright (C) 2021 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,84 +14,38 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.systemui.qs.tileimpl.ButtonRelativeLayout
+<com.android.systemui.qs.tileimpl.IgnorableChildLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clipChildren="false"
android:clipToPadding="false"
- android:paddingTop="12dp">
- <LinearLayout
- android:id="@+id/label_group"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:gravity="center"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:orientation="horizontal">
- <Space
- android:id="@+id/expand_space"
- android:layout_width="22dp"
- android:layout_height="0dp"
- android:visibility="gone" />
-
- <TextView
- android:id="@+id/tile_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clickable="false"
- android:padding="0dp"
- android:gravity="center"
- android:ellipsize="marquee"
- android:textAppearance="@style/TextAppearance.QS.TileLabel"/>
-
- <ImageView android:id="@+id/restricted_padlock"
- android:layout_width="@dimen/qs_tile_text_size"
- android:layout_height="match_parent"
- android:paddingBottom="@dimen/qs_tile_text_size"
- android:src="@drawable/ic_info"
- android:layout_marginLeft="@dimen/restricted_padlock_pading"
- android:scaleType="centerInside"
- android:visibility="gone" />
+ android:orientation="vertical"
+ android:layout_marginStart="@dimen/qs_label_container_margin"
+ android:layout_marginEnd="0dp"
+ android:layout_gravity="center_vertical | start">
- <ImageView
- android:id="@+id/expand_indicator"
- android:layout_marginStart="4dp"
- android:layout_width="18dp"
- android:layout_height="match_parent"
- android:src="@drawable/qs_dual_tile_caret"
- android:tint="?android:attr/textColorPrimary"
- android:visibility="gone" />
- </LinearLayout>
+ <TextView
+ android:id="@+id/tile_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start"
+ android:textDirection="locale"
+ android:ellipsize="marquee"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.QS.TileLabel"/>
<TextView
android:id="@+id/app_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignStart="@id/label_group"
- android:layout_alignEnd="@id/label_group"
- android:layout_below="@id/label_group"
- android:clickable="false"
+ android:gravity="start"
+ android:textDirection="locale"
android:ellipsize="marquee"
android:singleLine="true"
- android:padding="0dp"
android:visibility="gone"
- android:gravity="center"
android:textAppearance="@style/TextAppearance.QS.TileLabel.Secondary"
android:textColor="?android:attr/textColorSecondary"/>
- <View
- android:id="@+id/underline"
- android:layout_width="30dp"
- android:layout_height="1dp"
- android:layout_marginTop="2dp"
- android:layout_alignStart="@id/label_group"
- android:layout_alignEnd="@id/label_group"
- android:layout_below="@id/label_group"
- android:visibility="gone"
- android:alpha="?android:attr/disabledAlpha"
- android:background="?android:attr/colorForeground"/>
-
-</com.android.systemui.qs.tileimpl.ButtonRelativeLayout>
+</com.android.systemui.qs.tileimpl.IgnorableChildLinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_tile_side_icon.xml b/packages/SystemUI/res/layout/qs_tile_side_icon.xml
new file mode 100644
index 000000000000..9f9af9d21db1
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_tile_side_icon.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/qs_label_container_margin"
+ android:layout_gravity="center_vertical | end"
+>
+ <ImageView
+ android:id="@+id/customDrawable"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/qs_icon_size"
+ android:layout_marginEnd="@dimen/qs_drawable_end_margin"
+ android:adjustViewBounds="true"
+ android:scaleType="fitCenter"
+ android:visibility="gone"
+ />
+
+ <ImageView
+ android:id="@+id/chevron"
+ android:layout_width="@dimen/qs_icon_size"
+ android:layout_height="@dimen/qs_icon_size"
+ android:src="@*android:drawable/ic_chevron_end"
+ android:visibility="gone"
+ android:importantForAccessibility="no"
+ />
+</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index d8bb7e607a5d..f0229a648612 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -36,19 +36,16 @@
android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock" />
-
- <View
- android:layout_height="match_parent"
- android:layout_width="0dp"
- android:layout_weight="1"
- />
+ android:textAppearance="@style/TextAppearance.QS.Status" />
<include layout="@layout/qs_carrier_group"
android:id="@+id/carrier_group"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="match_parent"
+ android:layout_weight="1"
android:minHeight="48dp"
+ android:minWidth="48dp"
+ android:layout_marginStart="8dp"
android:layout_gravity="end|center_vertical"
android:focusable="false"/>
diff --git a/packages/SystemUI/res/layout/quick_settings_security_footer.xml b/packages/SystemUI/res/layout/quick_settings_security_footer.xml
index de65fa0511bb..ce7f82780dfe 100644
--- a/packages/SystemUI/res/layout/quick_settings_security_footer.xml
+++ b/packages/SystemUI/res/layout/quick_settings_security_footer.xml
@@ -23,7 +23,7 @@
android:padding="@dimen/qs_footer_padding"
android:gravity="center_vertical"
android:layout_gravity="center_vertical|center_horizontal"
- android:layout_marginVertical="@dimen/qs_security_footer_vertical_margin"
+ android:layout_marginBottom="@dimen/qs_footers_margin_bottom"
android:background="@drawable/qs_security_footer_background"
systemui:singleLineHeight="@dimen/qs_security_footer_single_line_height"
systemui:textViewId="@id/footer_text"
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index bb540990ccb7..d8f0742e1878 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -52,7 +52,6 @@
android:layout_below="@id/quick_qs_status_icons"
android:layout_marginTop="8dp"
android:accessibilityTraversalAfter="@id/quick_qs_status_icons"
- android:accessibilityTraversalBefore="@id/expand_indicator"
android:clipChildren="false"
android:clipToPadding="false"
android:focusable="true"
diff --git a/packages/SystemUI/res/layout/wallet_card_view.xml b/packages/SystemUI/res/layout/wallet_card_view.xml
index 5fd556d1fe25..b5a6601062a8 100644
--- a/packages/SystemUI/res/layout/wallet_card_view.xml
+++ b/packages/SystemUI/res/layout/wallet_card_view.xml
@@ -24,6 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginHorizontal="@dimen/card_margin"
+ android:layout_marginBottom="@dimen/card_margin"
android:foreground="?android:attr/selectableItemBackground"
app:cardBackgroundColor="@android:color/transparent"
app:cardElevation="12dp">
@@ -32,16 +33,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
- android:contentDescription="@null"
- android:scaleType="fitXY"/>
- <ImageView
- android:id="@+id/add_card_logo"
- android:layout_width="28dp"
- android:layout_height="28dp"
android:layout_gravity="center"
- android:drawable="@drawable/ic_qs_plus"
android:contentDescription="@null"
- android:scaleType="fitCenter"
- android:visibility="gone"/>
+ android:scaleType="fitXY"/>
</com.android.systemui.wallet.ui.WalletCardView>
</FrameLayout>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 46e7d71c4c36..96809dc2ff55 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -32,7 +32,7 @@
<dimen name="qs_security_footer_single_line_height">@*android:dimen/quick_qs_offset_height</dimen>
<dimen name="qs_footer_padding">14dp</dimen>
- <dimen name="qs_security_footer_vertical_margin">0dp</dimen>
+ <dimen name="qs_footers_margin_bottom">0dp</dimen>
<dimen name="qs_security_footer_background_inset">12dp</dimen>
<dimen name="qs_security_footer_corner_radius">28dp</dimen>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index ee25a1059d63..4d9b9e1ed87a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -255,7 +255,6 @@
<!-- media -->
<color name="media_disabled">#80ffffff</color>
<color name="media_seamless_border">?android:attr/colorAccent</color>
- <color name="media_divider">#1d000000</color>
<!-- controls -->
<color name="control_primary_text">#E6FFFFFF</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 073913d9a1b3..e3139b8fc854 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -555,7 +555,9 @@
<dimen name="qs_icon_size">20dp</dimen>
<dimen name="qs_label_container_margin">10dp</dimen>
<dimen name="qs_quick_tile_size">60dp</dimen>
- <dimen name="qs_quick_tile_padding">12dp</dimen>
+ <dimen name="qs_tile_padding">12dp</dimen>
+ <dimen name="qs_tile_start_padding">16dp</dimen>
+ <dimen name="qs_drawable_end_margin">4dp</dimen>
<dimen name="qs_header_gear_translation">16dp</dimen>
<dimen name="qs_header_tile_margin_bottom">18dp</dimen>
<dimen name="qs_page_indicator_width">16dp</dimen>
@@ -564,8 +566,6 @@
Scaled @dimen/qs_page_indicator-width by .4f.
-->
<dimen name="qs_page_indicator_dot_width">6.4dp</dimen>
- <dimen name="qs_tile_side_label_padding">12dp</dimen>
- <dimen name="qs_tile_icon_size">24dp</dimen>
<dimen name="qs_tile_text_size">14sp</dimen>
<dimen name="qs_tile_divider_height">1dp</dimen>
<dimen name="qs_panel_padding">16dp</dimen>
@@ -616,7 +616,7 @@
<dimen name="qs_footer_padding">20dp</dimen>
<dimen name="qs_security_footer_height">88dp</dimen>
<dimen name="qs_security_footer_single_line_height">48dp</dimen>
- <dimen name="qs_security_footer_vertical_margin">8dp</dimen>
+ <dimen name="qs_footers_margin_bottom">8dp</dimen>
<dimen name="qs_security_footer_background_inset">0dp</dimen>
<dimen name="qs_security_footer_corner_radius">28dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3480d10ce94c..4da2e3306519 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -690,11 +690,11 @@
<!-- Announcement made when the screen stopped casting (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_casting_turned_off">Screen casting stopped.</string>
<!-- Content description of the work mode title in quick settings when off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_work_mode_off">Work mode off.</string>
+ <string name="accessibility_quick_settings_work_mode_off">Work mode paused.</string>
<!-- Content description of the work mode title in quick settings when on (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_work_mode_on">Work mode on.</string>
<!-- Announcement made when the work mode changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_work_mode_changed_off">Work mode turned off.</string>
+ <string name="accessibility_quick_settings_work_mode_changed_off">Work mode turned paused.</string>
<!-- Announcement made when the work mode changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_work_mode_changed_on">Work mode turned on.</string>
<!-- Announcement made when the Data Saver changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -954,6 +954,8 @@
off. "Work profile" means a separate profile on a user's phone that's specifically for their
work apps and managed by their company. "Work" is used as an adjective. [CHAR LIMIT=NONE] -->
<string name="quick_settings_work_mode_label">Work profile</string>
+ <!-- QuickSettings: Secondary label for work mode tile when it's off. [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_work_mode_paused">Paused</string>
<!-- QuickSettings: Label for the toggle to activate Night display (renamed "Night Light" with title caps). [CHAR LIMIT=20] -->
<string name="quick_settings_night_display_label">Night Light</string>
<!-- QuickSettings: Secondary text for when the Night Light will be enabled at sunset. [CHAR LIMIT=20] -->
@@ -2187,6 +2189,9 @@
<!-- The tile in quick settings is unavailable. [CHAR LIMIT=32] -->
<string name="tile_unavailable">Unavailable</string>
+ <!-- The tile in quick settings is disabled by a device administration policy [CHAR LIMIT=32] -->
+ <string name="tile_disabled">Disabled</string>
+
<!-- SysUI Tuner: Button that leads to the navigation bar customization screen [CHAR LIMIT=60] -->
<string name="nav_bar">Navigation bar</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index aa1f954cb27a..7db9decbe78a 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -226,13 +226,21 @@
<item name="android:textSize">@dimen/qs_tile_text_size</item>
</style>
- <style name="TextAppearance.QS.Status" parent="TextAppearance.QS.TileLabel.Secondary">
- <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
- <item name="android:textColor">@color/dark_mode_qs_icon_color_single_tone</item>
+ <style name="TextAppearance.QS.Status">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:letterSpacing">0.01</item>
+ <item name="android:lineHeight">20sp</item>
+ </style>
+
+ <style name="TextAppearance.QS.Status.NoCarrierText">
+ <item name="android:textColor">?android:attr/textColorTertiary</item>
</style>
<style name="TextAppearance.QS.Build">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
<item name="android:textSize">14sp</item>
</style>
@@ -389,7 +397,6 @@
<style name="Theme.SystemUI.QuickSettings" parent="@*android:style/Theme.DeviceDefault">
<item name="lightIconTheme">@style/QSIconTheme</item>
<item name="darkIconTheme">@style/QSIconTheme</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:colorError">@*android:color/error_color_material_dark</item>
<item name="android:windowIsFloating">true</item>
<item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
index 195d00606693..b7344fbc6dda 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -45,7 +45,7 @@ abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView>
@NonNull final StatusBar mStatusBar;
@NonNull final DumpManager mDumpManger;
- private boolean mNotificationShadeExpanded;
+ boolean mNotificationShadeExpanded;
protected UdfpsAnimationViewController(
T view,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index 37ea251c08b8..52576a7cd4e3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -40,12 +40,14 @@ import com.android.systemui.statusbar.StatusBarState;
public class UdfpsKeyguardView extends UdfpsAnimationView {
private final UdfpsKeyguardDrawable mFingerprintDrawable;
private ImageView mFingerprintView;
- private int mWallpaperTexColor;
+ private int mWallpaperTextColor;
private int mStatusBarState;
// used when highlighting fp icon:
private int mTextColorPrimary;
private ImageView mBgProtection;
+ boolean mUdfpsRequested;
+ int mUdfpsRequestedColor;
private AnimatorSet mAnimatorSet;
private int mAlpha; // 0-255
@@ -63,10 +65,11 @@ public class UdfpsKeyguardView extends UdfpsAnimationView {
mBgProtection = findViewById(R.id.udfps_keyguard_fp_bg);
- mWallpaperTexColor = Utils.getColorAttrDefaultColor(mContext,
+ mWallpaperTextColor = Utils.getColorAttrDefaultColor(mContext,
R.attr.wallpaperTextColorAccent);
mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext,
android.R.attr.textColorPrimary);
+ mUdfpsRequested = false;
}
@Override
@@ -90,11 +93,31 @@ public class UdfpsKeyguardView extends UdfpsAnimationView {
return true;
}
+ void requestUdfps(boolean request, int color) {
+ if (request) {
+ mUdfpsRequestedColor = color;
+ } else {
+ mUdfpsRequestedColor = -1;
+ }
+ mUdfpsRequested = request;
+ updateColor();
+ }
+
void setStatusBarState(int statusBarState) {
mStatusBarState = statusBarState;
- if (!isShadeLocked()) {
- mFingerprintView.setAlpha(1f);
- mFingerprintDrawable.setLockScreenColor(mWallpaperTexColor);
+ updateColor();
+ }
+
+ void updateColor() {
+ mFingerprintView.setAlpha(1f);
+ mFingerprintDrawable.setLockScreenColor(getColor());
+ }
+
+ private int getColor() {
+ if (mUdfpsRequested && mUdfpsRequestedColor != -1) {
+ return mUdfpsRequestedColor;
+ } else {
+ return mWallpaperTextColor;
}
}
@@ -142,7 +165,7 @@ public class UdfpsKeyguardView extends UdfpsAnimationView {
} else {
// update icon color
fpIconAnim = new ValueAnimator();
- fpIconAnim.setIntValues(mWallpaperTexColor, mTextColorPrimary);
+ fpIconAnim.setIntValues(getColor(), mTextColorPrimary);
fpIconAnim.setEvaluator(new ArgbEvaluator());
fpIconAnim.addUpdateListener(valueAnimator -> mFingerprintDrawable.setLockScreenColor(
(Integer) valueAnimator.getAnimatedValue()));
@@ -170,10 +193,6 @@ public class UdfpsKeyguardView extends UdfpsAnimationView {
return mStatusBarState == StatusBarState.SHADE_LOCKED;
}
- boolean isHome() {
- return mStatusBarState == StatusBarState.SHADE;
- }
-
/**
* Animates out the bg protection circle behind the fp icon to unhighlight the icon.
*/
@@ -193,7 +212,7 @@ public class UdfpsKeyguardView extends UdfpsAnimationView {
} else {
// update icon color
fpIconAnim = new ValueAnimator();
- fpIconAnim.setIntValues(mTextColorPrimary, mWallpaperTexColor);
+ fpIconAnim.setIntValues(mTextColorPrimary, getColor());
fpIconAnim.setEvaluator(new ArgbEvaluator());
fpIconAnim.addUpdateListener(valueAnimator -> mFingerprintDrawable.setLockScreenColor(
(Integer) valueAnimator.getAnimatedValue()));
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 4d2f809531fd..33d0d0c5b5ff 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -57,6 +57,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
@Nullable private Runnable mCancelRunnable;
private boolean mShowingUdfpsBouncer;
+ private boolean mUdfpsRequested;
private boolean mQsExpanded;
private boolean mFaceDetectRunning;
private boolean mHintShown;
@@ -70,6 +71,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
* {@link KeyguardBouncer#EXPANSION_HIDDEN} (1f)
*/
private float mInputBouncerHiddenAmount;
+ private boolean mIsBouncerVisible;
protected UdfpsKeyguardViewController(
@NonNull UdfpsKeyguardView view,
@@ -105,6 +107,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
mStatusBarStateController.addCallback(mStateListener);
+ mUdfpsRequested = false;
mStatusBarState = mStatusBarStateController.getState();
mQsExpanded = mKeyguardViewManager.isQsExpanded();
mKeyguardIsVisible = mKeyguardUpdateMonitor.isKeyguardVisible();
@@ -113,6 +116,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
updatePauseAuth();
mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+ mIsBouncerVisible = mKeyguardViewManager.bouncerIsOrWillBeShowing();
}
@Override
@@ -137,11 +141,15 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
pw.println("mShowingUdfpsBouncer=" + mShowingUdfpsBouncer);
pw.println("mFaceDetectRunning=" + mFaceDetectRunning);
pw.println("mTransitioningFromHomeToKeyguard=" + mTransitioningFromHome);
- pw.println("mStatusBarState" + StatusBarState.toShortString(mStatusBarState));
+ pw.println("mStatusBarState=" + StatusBarState.toShortString(mStatusBarState));
pw.println("mQsExpanded=" + mQsExpanded);
pw.println("mKeyguardVisible=" + mKeyguardIsVisible);
+ pw.println("mIsBouncerVisible=" + mIsBouncerVisible);
pw.println("mInputBouncerHiddenAmount=" + mInputBouncerHiddenAmount);
pw.println("mAlpha=" + mView.getAlpha());
+ pw.println("mUdfpsRequested=" + mUdfpsRequested);
+ pw.println("mView.mUdfpsRequested=" + mView.mUdfpsRequested);
+ pw.println("mView.mUdfpsRequestedColor=" + mView.mUdfpsRequestedColor);
}
/**
@@ -173,6 +181,12 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
return false;
}
+ if (mUdfpsRequested && !mNotificationShadeExpanded
+ && (!mIsBouncerVisible
+ || mInputBouncerHiddenAmount != KeyguardBouncer.EXPANSION_VISIBLE)) {
+ return false;
+ }
+
if (mStatusBarState != KEYGUARD) {
return true;
}
@@ -333,6 +347,13 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
}
@Override
+ public void requestUdfps(boolean request, int color) {
+ mUdfpsRequested = request;
+ mView.requestUdfps(request, color);
+ updatePauseAuth();
+ }
+
+ @Override
public boolean isAnimating() {
return mView.isAnimating();
}
@@ -356,6 +377,12 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
}
@Override
+ public void onBouncerVisibilityChanged() {
+ mIsBouncerVisible = mKeyguardViewManager.bouncerIsOrWillBeShowing();
+ updatePauseAuth();
+ }
+
+ @Override
public void dump(PrintWriter pw) {
pw.println(getTag());
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index bd3b899adee7..30429fbf8cfa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -38,7 +38,6 @@ import android.provider.Settings;
import android.service.notification.ZenModeConfig;
import android.text.TextUtils;
import android.text.style.StyleSpan;
-import android.util.Log;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -53,8 +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.dagger.SysUIComponent;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.StatusBarState;
@@ -65,8 +62,6 @@ import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
@@ -318,25 +313,7 @@ public class KeyguardSliceProvider extends SliceProvider implements
mPendingIntent = PendingIntent.getActivity(getContext(), 0,
new Intent(getContext(), KeyguardSliceProvider.class),
PendingIntent.FLAG_IMMUTABLE);
- try {
- //TODO(b/168778439): Remove this whole try catch. This is for debugging in dogfood.
- mMediaManager.addCallback(this);
- } catch (NullPointerException e) {
- // We are sometimes failing to set the media manager. Why?
- Log.w(TAG, "Failed to setup mMediaManager. Trying again.");
- SysUIComponent rootComponent = SystemUIFactory.getInstance().getSysUIComponent();
- try {
- Method injectMethod = rootComponent.getClass()
- .getMethod("inject", getClass());
- injectMethod.invoke(rootComponent, this);
- Log.w(TAG, "mMediaManager is now: " + mMediaManager);
- } catch (NoSuchMethodException ex) {
- Log.e(TAG, "Failed to find inject method for KeyguardSliceProvider", ex);
- } catch (IllegalAccessException | InvocationTargetException ex) {
- Log.e(TAG, "Failed to call inject", ex);
- }
- throw e;
- }
+ mMediaManager.addCallback(this);
mStatusBarStateController.addCallback(this);
mNextAlarmController.addCallback(this);
mZenModeController.addCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index be96ba8a09d7..1010b6a0b97b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -398,14 +398,8 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
// Fade in the security footer and the divider as we reach the final position
builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY);
builder.addFloat(mSecurityFooter.getView(), "alpha", 0, 1);
- if (mQsPanelController.getDivider() != null) {
- builder.addFloat(mQsPanelController.getDivider(), "alpha", 0, 1);
- }
mAllPagesDelayedAnimator = builder.build();
mAllViews.add(mSecurityFooter.getView());
- if (mQsPanelController.getDivider() != null) {
- mAllViews.add(mQsPanelController.getDivider());
- }
translationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
mTranslationYAnimator = translationYBuilder.build();
if (mQQSTileHeightAnimator != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 40967ede057e..57438d189b22 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -45,7 +45,6 @@ import com.android.settingslib.Utils;
import com.android.settingslib.development.DevelopmentSettingsEnabler;
import com.android.settingslib.drawable.UserIconDrawable;
import com.android.systemui.R;
-import com.android.systemui.R.dimen;
import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.statusbar.phone.MultiUserSwitch;
import com.android.systemui.statusbar.phone.SettingsButton;
@@ -140,7 +139,7 @@ public class QSFooterView extends FrameLayout {
void updateAnimator(int width, int numTiles) {
int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size)
- - mContext.getResources().getDimensionPixelSize(dimen.qs_quick_tile_padding);
+ - mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_padding);
int remaining = (width - numTiles * size) / (numTiles - 1);
int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
@@ -167,6 +166,9 @@ public class QSFooterView extends FrameLayout {
private void updateResources() {
updateFooterAnimator();
+ MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
+ lp.bottomMargin = getResources().getDimensionPixelSize(R.dimen.qs_footers_margin_bottom);
+ setLayoutParams(lp);
mTunerIconTranslation = mContext.getResources()
.getDimensionPixelOffset(R.dimen.qs_footer_tuner_icon_translation);
mTunerIcon.setTranslationX(isLayoutRtl() ? -mTunerIconTranslation : mTunerIconTranslation);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 4e16b7414ff1..7062e8cea207 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -84,8 +84,6 @@ public class QSPanel extends LinearLayout implements Tunable {
@Nullable
protected View mFooter;
- @Nullable
- protected View mDivider;
@Nullable
private ViewGroup mHeaderContainer;
@@ -327,7 +325,6 @@ public class QSPanel extends LinearLayout implements Tunable {
protected void onFinishInflate() {
super.onFinishInflate();
mFooter = findViewById(R.id.qs_footer);
- mDivider = findViewById(R.id.divider);
}
private void updateHorizontalLinearLayoutMargins() {
@@ -602,11 +599,6 @@ public class QSPanel extends LinearLayout implements Tunable {
return mTileLayout;
}
- @Nullable
- public View getDivider() {
- return mDivider;
- }
-
/** */
public void setContentMargins(int startMargin, int endMargin, ViewGroup mediaHostView) {
// Only some views actually want this content padding, others want to go all the way
@@ -614,12 +606,6 @@ public class QSPanel extends LinearLayout implements Tunable {
mContentMarginStart = startMargin;
mContentMarginEnd = endMargin;
updateMediaHostContentMargins(mediaHostView);
- updateDividerMargin();
- }
-
- private void updateDividerMargin() {
- if (mDivider == null) return;
- updateMargins(mDivider, mContentMarginStart, mContentMarginEnd);
}
/**
@@ -712,7 +698,6 @@ public class QSPanel extends LinearLayout implements Tunable {
}
private void updateMargins(ViewGroup mediaHostView) {
- updateDividerMargin();
updateMediaHostContentMargins(mediaHostView);
updateHorizontalLinearLayoutMargins();
updatePadding();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index fff3d1fe4b5f..ac92d4fe44e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -284,10 +284,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
return mView.getBrightnessView();
}
- public View getDivider() {
- return mView.getDivider();
- }
-
/** */
public void setPageListener(PagedTileLayout.PageListener listener) {
mView.setPageListener(listener);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index e40f2936de51..170785ca7aab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -22,7 +22,6 @@ import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLA
import android.content.ComponentName;
import android.content.res.Configuration;
import android.metrics.LogMaker;
-import android.view.View;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
@@ -294,13 +293,6 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
boolean switchTileLayout(boolean force) {
/** Whether or not the QuickQSPanel currently contains a media player. */
boolean horizontal = shouldUseHorizontalLayout();
- if (mView.getDivider() != null) {
- if (!horizontal && mUsingMediaPlayer && mMediaHost.getVisible()) {
- mView.getDivider().setVisibility(View.VISIBLE);
- } else {
- mView.getDivider().setVisibility(View.GONE);
- }
- }
if (horizontal != mUsingHorizontalLayout || force) {
mUsingHorizontalLayout = horizontal;
for (QSPanelControllerBase.TileRecord record : mRecords) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index baf781d38441..04e32a10db17 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -128,11 +128,10 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
int padding = r.getDimensionPixelSize(R.dimen.qs_footer_padding);
mRootView.setPaddingRelative(padding, padding, padding, padding);
- int verticalMargin = r.getDimensionPixelSize(R.dimen.qs_security_footer_vertical_margin);
+ int bottomMargin = r.getDimensionPixelSize(R.dimen.qs_footers_margin_bottom);
ViewGroup.MarginLayoutParams lp =
(ViewGroup.MarginLayoutParams) mRootView.getLayoutParams();
- lp.topMargin = verticalMargin;
- lp.bottomMargin = verticalMargin;
+ lp.bottomMargin = bottomMargin;
lp.width = r.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT
? MATCH_PARENT : WRAP_CONTENT;
mRootView.setLayoutParams(lp);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 63733b392631..c3458feb5dfe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -285,8 +285,6 @@ public class QuickQSPanel extends QSPanel {
if (record.tileView.getVisibility() == GONE) continue;
previousView = record.tileView.updateAccessibilityOrder(previousView);
}
- mRecords.get(mRecords.size() - 1).tileView.setAccessibilityTraversalBefore(
- R.id.expand_indicator);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
deleted file mode 100644
index ce8f6c1737d7..000000000000
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2016 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.qs.customize;
-
-import android.content.Context;
-import android.view.View;
-
-import com.android.systemui.plugins.qs.QSIconView;
-import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.qs.tileimpl.QSTileView;
-
-public class CustomizeTileView extends QSTileView implements TileAdapter.CustomizeView {
- private boolean mShowAppLabel;
-
- public CustomizeTileView(Context context, QSIconView icon) {
- super(context, icon);
- }
-
- @Override
- public void setShowAppLabel(boolean showAppLabel) {
- mShowAppLabel = showAppLabel;
- mSecondLine.setVisibility(showAppLabel ? View.VISIBLE : View.GONE);
- mLabel.setSingleLine(showAppLabel);
- }
-
- @Override
- protected void handleStateChanged(QSTile.State state) {
- super.handleStateChanged(state);
- mSecondLine.setVisibility(mShowAppLabel ? View.VISIBLE : View.GONE);
- }
-
- @Override
- protected boolean animationsEnabled() {
- return false;
- }
-
- @Override
- public boolean isLongClickable() {
- return false;
- }
-
- @Override
- public void changeState(QSTile.State state) {
- handleStateChanged(state);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt
new file mode 100644
index 000000000000..a316e6aa5f1b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 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.qs.customize
+
+import android.content.Context
+import android.text.TextUtils
+import com.android.systemui.plugins.qs.QSIconView
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.tileimpl.QSTileViewImpl
+
+/**
+ * Class for displaying tiles in [QSCustomizer] with the new design (labels on the side).
+ */
+class CustomizeTileView(
+ context: Context,
+ icon: QSIconView
+) : QSTileViewImpl(context, icon, collapsed = false) {
+
+ var showAppLabel = false
+ set(value) {
+ field = value
+ secondaryLabel.visibility = getVisibilityState(secondaryLabel.text)
+ }
+
+ var showSideView = true
+ set(value) {
+ field = value
+ if (!showSideView) sideView.visibility = GONE
+ }
+
+ override fun handleStateChanged(state: QSTile.State) {
+ super.handleStateChanged(state)
+ showRippleEffect = false
+ secondaryLabel.visibility = getVisibilityState(state.secondaryLabel)
+ if (!showSideView) sideView.visibility = GONE
+ }
+
+ private fun getVisibilityState(text: CharSequence?): Int {
+ return if (showAppLabel && !TextUtils.isEmpty(text)) {
+ VISIBLE
+ } else {
+ GONE
+ }
+ }
+
+ override fun animationsEnabled(): Boolean {
+ return false
+ }
+
+ override fun isLongClickable(): Boolean {
+ return false
+ }
+
+ fun changeState(state: QSTile.State) {
+ handleStateChanged(state)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
deleted file mode 100644
index 7977b4904a7d..000000000000
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.android.systemui.qs.customize
-
-import android.content.Context
-import android.view.View
-import com.android.systemui.plugins.qs.QSIconView
-import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.qs.tileimpl.QSTileViewHorizontal
-
-/**
- * Class for displaying tiles in [QSCustomizer] with the new design (labels on the side).
- *
- * This is a class parallel to [CustomizeTileView], but inheriting from [QSTileViewHorizontal].
- */
-class CustomizeTileViewHorizontal(
- context: Context,
- icon: QSIconView
-) : QSTileViewHorizontal(context, icon, collapsed = false),
- TileAdapter.CustomizeView {
-
- private var showAppLabel = false
-
- override fun setShowAppLabel(showAppLabel: Boolean) {
- this.showAppLabel = showAppLabel
- mSecondLine.visibility = if (showAppLabel) View.VISIBLE else View.GONE
- mLabel.isSingleLine = showAppLabel
- }
-
- override fun handleStateChanged(state: QSTile.State) {
- super.handleStateChanged(state)
- mShowRippleEffect = false
- mSecondLine.visibility = if (showAppLabel) View.VISIBLE else View.GONE
- }
-
- override fun animationsEnabled(): Boolean {
- return false
- }
-
- override fun isLongClickable(): Boolean {
- return false
- }
-
- override fun changeState(state: QSTile.State) {
- handleStateChanged(state)
- }
-} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 50805330cf1f..fe0e56c4869e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -43,7 +43,6 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSEditEvent;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.customize.TileAdapter.Holder;
@@ -53,7 +52,7 @@ import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.dagger.QSThemedContext;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
-import com.android.systemui.qs.tileimpl.QSTileView;
+import com.android.systemui.qs.tileimpl.QSTileViewImpl;
import java.util.ArrayList;
import java.util.List;
@@ -280,7 +279,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
}
FrameLayout frame = (FrameLayout) inflater.inflate(R.layout.qs_customize_tile_frame, parent,
false);
- View view = new CustomizeTileViewHorizontal(context, new QSIconViewImpl(context));
+ View view = new CustomizeTileView(context, new QSIconViewImpl(context));
frame.addView(view);
return new Holder(frame);
}
@@ -367,6 +366,8 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
// The holder has a tileView, therefore this call is not null
holder.getTileAsCustomizeView().changeState(info.state);
holder.getTileAsCustomizeView().setShowAppLabel(position > mEditIndex && !info.isSystem);
+ // Don't show the side view for third party tiles, as we don't have the actual state.
+ holder.getTileAsCustomizeView().setShowSideView(position < mEditIndex || info.isSystem);
holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
holder.mTileView.setClickable(true);
holder.mTileView.setOnClickListener(null);
@@ -545,15 +546,12 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
}
public class Holder extends ViewHolder {
- private QSTileView mTileView;
+ private QSTileViewImpl mTileView;
public Holder(View itemView) {
super(itemView);
if (itemView instanceof FrameLayout) {
- mTileView = (QSTileView) ((FrameLayout) itemView).getChildAt(0);
- if (mTileView instanceof CustomizeTileView) {
- mTileView.setBackground(null);
- }
+ mTileView = (QSTileViewImpl) ((FrameLayout) itemView).getChildAt(0);
mTileView.getIcon().disableAnimation();
mTileView.setTag(this);
ViewCompat.setAccessibilityDelegate(mTileView, mAccessibilityDelegate);
@@ -561,8 +559,8 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
}
@Nullable
- public CustomizeView getTileAsCustomizeView() {
- return (CustomizeView) mTileView;
+ public CustomizeTileView getTileAsCustomizeView() {
+ return (CustomizeTileView) mTileView;
}
public void clearDrag() {
@@ -570,8 +568,8 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
if (mTileView instanceof CustomizeTileView) {
mTileView.findViewById(R.id.tile_label).clearAnimation();
mTileView.findViewById(R.id.tile_label).setAlpha(1);
- mTileView.getAppLabel().clearAnimation();
- mTileView.getAppLabel().setAlpha(.6f);
+ mTileView.getSecondaryLabel().clearAnimation();
+ mTileView.getSecondaryLabel().setAlpha(.6f);
}
}
@@ -584,7 +582,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
mTileView.findViewById(R.id.tile_label).animate()
.setDuration(DRAG_LENGTH)
.alpha(0);
- mTileView.getAppLabel().animate()
+ mTileView.getSecondaryLabel().animate()
.setDuration(DRAG_LENGTH)
.alpha(0);
}
@@ -599,7 +597,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
mTileView.findViewById(R.id.tile_label).animate()
.setDuration(DRAG_LENGTH)
.alpha(1);
- mTileView.getAppLabel().animate()
+ mTileView.getSecondaryLabel().animate()
.setDuration(DRAG_LENGTH)
.alpha(.6f);
}
@@ -765,7 +763,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
int position = mCurrentDrag.getAdapterPosition();
if (position == RecyclerView.NO_POSITION) return;
TileInfo info = mTiles.get(position);
- ((CustomizeView) mCurrentDrag.mTileView).setShowAppLabel(
+ ((CustomizeTileView) mCurrentDrag.mTileView).setShowAppLabel(
position > mEditIndex && !info.isSystem);
mCurrentDrag.stopDrag();
mCurrentDrag = null;
@@ -825,9 +823,4 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
public void onSwiped(ViewHolder viewHolder, int direction) {
}
};
-
- interface CustomizeView {
- void setShowAppLabel(boolean showAppLabel);
- void changeState(@NonNull QSTile.State state);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ButtonRelativeLayout.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ButtonRelativeLayout.java
deleted file mode 100644
index 962537a01fe2..000000000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ButtonRelativeLayout.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2017 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.qs.tileimpl;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.Button;
-import android.widget.RelativeLayout;
-
-/**
- * Used for QS tile labels
- */
-public class ButtonRelativeLayout extends RelativeLayout {
-
- private View mIgnoredView;
-
- public ButtonRelativeLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public CharSequence getAccessibilityClassName() {
- return Button.class.getName();
- }
-
- /**
- * Set a view to be ignored for measure.
- *
- * The view will be measured and laid out, but its size will be subtracted from the total size
- * of this view. It assumes that this view only contributes vertical height.
- */
- public void setIgnoredView(View view) {
- if (mIgnoredView == null || mIgnoredView.getParent() == this) {
- mIgnoredView = view;
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (mIgnoredView != null && mIgnoredView.getVisibility() != GONE) {
- int height = mIgnoredView.getMeasuredHeight();
- MarginLayoutParams lp = (MarginLayoutParams) mIgnoredView.getLayoutParams();
- height = height - lp.bottomMargin - lp.topMargin;
- setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() - height);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt
new file mode 100644
index 000000000000..705576059b58
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 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.qs.tileimpl
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.LinearLayout
+
+/**
+ * [LinearLayout] that can ignore the last child for measuring.
+ *
+ * The view is measured as regularlt, then if [ignoreLastView] is true:
+ * * In [LinearLayout.VERTICAL] orientation, the height of the last view is subtracted from the
+ * final measured height.
+ * * In [LinearLayout.HORIZONTAL] orientation, the width of the last view is subtracted from the
+ * final measured width.
+ *
+ * This allows to measure the view and position it where it should, without it amounting to the
+ * total size (only in the direction of layout).
+ */
+class IgnorableChildLinearLayout @JvmOverloads constructor(
+ context: Context,
+ attributeSet: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : LinearLayout(context, attributeSet, defStyleAttr, defStyleRes) {
+
+ var ignoreLastView = false
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ if (ignoreLastView && childCount > 0) {
+ val lastView = getChildAt(childCount - 1)
+ val lp = lastView.layoutParams as MarginLayoutParams
+ if (orientation == VERTICAL) {
+ val height = lastView.measuredHeight + lp.bottomMargin + lp.topMargin
+ setMeasuredDimension(measuredWidth, measuredHeight - height)
+ } else {
+ val width = lastView.measuredWidth + lp.leftMargin + lp.rightMargin
+ setMeasuredDimension(measuredWidth - width, measuredHeight)
+ }
+ }
+ }
+} \ No newline at end of file
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 3437dd595152..8f7c493417ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -243,6 +243,6 @@ public class QSFactoryImpl implements QSFactory {
@Override
public QSTileView createTileView(Context context, QSTile tile, boolean collapsedView) {
QSIconView icon = tile.createTileView(context);
- return new QSTileViewHorizontal(context, icon, collapsedView);
+ return new QSTileViewImpl(context, icon, collapsedView);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 31526bf8f5e1..8280f98114e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -14,22 +14,24 @@
package com.android.systemui.qs.tileimpl;
-import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.Animatable2.AnimationCallback;
import android.graphics.drawable.Drawable;
+import android.service.quicksettings.Tile;
+import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
@@ -43,7 +45,7 @@ public class QSIconViewImpl extends QSIconView {
public static final long QS_ANIM_LENGTH = 350;
protected final View mIcon;
- protected final int mIconSizePx;
+ protected int mIconSizePx;
private boolean mAnimationEnabled = true;
private int mState = -1;
private int mTint;
@@ -53,12 +55,18 @@ public class QSIconViewImpl extends QSIconView {
super(context);
final Resources res = context.getResources();
- mIconSizePx = res.getDimensionPixelSize(R.dimen.qs_tile_icon_size);
+ mIconSizePx = res.getDimensionPixelSize(R.dimen.qs_icon_size);
mIcon = createIcon();
addView(mIcon);
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mIconSizePx = getContext().getResources().getDimensionPixelSize(R.dimen.qs_icon_size);
+ }
+
public void disableAnimation() {
mAnimationEnabled = false;
}
@@ -169,7 +177,7 @@ public class QSIconViewImpl extends QSIconView {
}
protected int getColor(int state) {
- return getColorForState(getContext(), state);
+ return getIconColorForState(getContext(), state);
}
private void animateGrayScale(int fromColor, int toColor, ImageView iv,
@@ -229,4 +237,21 @@ public class QSIconViewImpl extends QSIconView {
protected final void layout(View child, int left, int top) {
child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
}
+
+ /**
+ * Color to tint the tile icon based on state
+ */
+ public static int getIconColorForState(Context context, int state) {
+ switch (state) {
+ case Tile.STATE_UNAVAILABLE:
+ return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorTertiary);
+ case Tile.STATE_INACTIVE:
+ return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary);
+ case Tile.STATE_ACTIVE:
+ return Utils.getColorAttrDefaultColor(context, android.R.attr.colorPrimary);
+ default:
+ Log.e("QSIconView", "Invalid state " + state);
+ return 0;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
deleted file mode 100644
index c973e8c6fc2c..000000000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * Copyright (C) 2017 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.qs.tileimpl;
-
-import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION;
-
-import static com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.RippleDrawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.PathShape;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.service.quicksettings.Tile;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.PathParser;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.Switch;
-
-import com.android.settingslib.Utils;
-import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSIconView;
-import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTile.BooleanState;
-
-public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
-
- private static final String TAG = "QSTileBaseView";
- private static final int ICON_MASK_ID = com.android.internal.R.string.config_icon_mask;
- protected final Handler mHandler = new H();
- private final int[] mLocInScreen = new int[2];
- protected final FrameLayout mIconFrame;
- protected QSIconView mIcon;
- protected RippleDrawable mRipple;
- protected Drawable mTileBackground;
- private String mAccessibilityClass;
- private boolean mTileState;
- protected boolean mCollapsedView;
- protected boolean mShowRippleEffect = true;
- private float mStrokeWidthActive;
- private float mStrokeWidthInactive;
-
- protected final ImageView mBg;
- private final int mColorActive;
- private final int mColorInactive;
- private int mCircleColor;
- private int mBgSize;
-
- private static final int INVALID = -1;
- private CharSequence mStateDescriptionDeltas = null;
- private CharSequence mLastStateDescription;
- private int mLastState = INVALID;
-
- public QSTileBaseView(Context context, QSIconView icon) {
- this(context, icon, false);
- }
-
- public QSTileBaseView(Context context, QSIconView icon, boolean collapsedView) {
- super(context);
- // Default to Quick Tile padding, and QSTileView will specify its own padding.
- int padding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
- mIconFrame = new FrameLayout(context);
- mStrokeWidthActive = context.getResources()
- .getDimension(com.android.internal.R.dimen.config_qsTileStrokeWidthActive);
- mStrokeWidthInactive = context.getResources()
- .getDimension(com.android.internal.R.dimen.config_qsTileStrokeWidthInactive);
- int size = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
- addView(mIconFrame, new LayoutParams(size, size));
- mBg = new ImageView(getContext());
- Path path = new Path(PathParser.createPathFromPathData(
- context.getResources().getString(ICON_MASK_ID)));
- float pathSize = AdaptiveIconDrawable.MASK_SIZE;
- PathShape p = new PathShape(path, pathSize, pathSize);
- ShapeDrawable d = new ShapeDrawable(p);
- d.setTintList(ColorStateList.valueOf(Color.TRANSPARENT));
- float backgroundStrokeWidth = context.getResources()
- .getDimension(R.dimen.qs_tile_icon_background_stroke_width);
- if (backgroundStrokeWidth > 0) {
- d.getPaint().setStyle(Paint.Style.STROKE);
- d.getPaint().setStrokeWidth(backgroundStrokeWidth);
- }
- int bgSize = context.getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size);
- d.setIntrinsicHeight(bgSize);
- d.setIntrinsicWidth(bgSize);
- mBg.setImageDrawable(d);
- FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(bgSize, bgSize, Gravity.CENTER);
- mIconFrame.addView(mBg, lp);
- mBg.setLayoutParams(lp);
- mIcon = icon;
- FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT,
- Gravity.CENTER);
- mIconFrame.addView(mIcon, params);
- mIconFrame.setClipChildren(false);
- mIconFrame.setClipToPadding(false);
-
- mTileBackground = newTileBackground();
- if (mTileBackground instanceof RippleDrawable) {
- setRipple((RippleDrawable) mTileBackground);
- }
- setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- setBackground(mTileBackground);
-
- mColorActive = Utils.getColorAttrDefaultColor(context,
- com.android.internal.R.attr.colorAccentPrimary);
- mColorInactive = Utils.getColorAttrDefaultColor(context, R.attr.offStateColor);
-
- setPadding(0, 0, 0, 0);
- setClipChildren(false);
- setClipToPadding(false);
- mCollapsedView = collapsedView;
- setFocusable(true);
- }
-
- public View getBgCircle() {
- return mBg;
- }
-
- protected Drawable newTileBackground() {
- final int[] attrs = new int[]{android.R.attr.selectableItemBackgroundBorderless};
- final TypedArray ta = getContext().obtainStyledAttributes(attrs);
- final Drawable d = ta.getDrawable(0);
- ta.recycle();
- return d;
- }
-
- private void setRipple(RippleDrawable tileBackground) {
- mRipple = tileBackground;
- if (getWidth() != 0) {
- updateRippleSize();
- }
- }
-
- protected void updateRippleSize() {
- // center the touch feedback on the center of the icon, and dial it down a bit
- final int cx = mIconFrame.getMeasuredWidth() / 2 + mIconFrame.getLeft();
- final int cy = mIconFrame.getMeasuredHeight() / 2 + mIconFrame.getTop();
- final int rad = (int) (mIcon.getHeight() * .85f);
- mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
- }
-
- @Override
- public void init(QSTile tile) {
- init(v -> tile.click(this), v -> tile.secondaryClick(this), view -> {
- tile.longClick(this);
- return true;
- });
- }
-
- public void init(OnClickListener click, OnClickListener secondaryClick,
- OnLongClickListener longClick) {
- setOnClickListener(click);
- setOnLongClickListener(longClick);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- if (mRipple != null) {
- updateRippleSize();
- }
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- // Avoid layers for this layout - we don't need them.
- return false;
- }
-
- /**
- * Update the accessibility order for this view.
- *
- * @param previousView the view which should be before this one
- * @return the last view in this view which is accessible
- */
- public View updateAccessibilityOrder(View previousView) {
- setAccessibilityTraversalAfter(previousView.getId());
- return this;
- }
-
- public void onStateChanged(QSTile.State state) {
- mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget();
- }
-
- private void updateStrokeShapeWidth(QSTile.State state) {
- Resources resources = getContext().getResources();
- if (!(mBg.getDrawable() instanceof ShapeDrawable)) {
- return;
- }
- ShapeDrawable d = (ShapeDrawable) mBg.getDrawable();
- d.getPaint().setStyle(Paint.Style.FILL);
- switch (state.state) {
- case Tile.STATE_INACTIVE:
- if (mStrokeWidthInactive >= 0) {
- d.getPaint().setStyle(Paint.Style.STROKE);
- d.getPaint().setStrokeWidth(mStrokeWidthInactive);
- }
- break;
- case Tile.STATE_ACTIVE:
- if (mStrokeWidthActive >= 0) {
- d.getPaint().setStyle(Paint.Style.STROKE);
- d.getPaint().setStrokeWidth(mStrokeWidthActive);
- }
- break;
- }
- }
-
- protected void handleStateChanged(QSTile.State state) {
- updateStrokeShapeWidth(state);
- int circleColor = getCircleColor(state.state);
- boolean allowAnimations = animationsEnabled();
- if (circleColor != mCircleColor) {
- if (allowAnimations) {
- ValueAnimator animator = ValueAnimator.ofArgb(mCircleColor, circleColor)
- .setDuration(QS_ANIM_LENGTH);
- animator.addUpdateListener(animation -> mBg.setImageTintList(ColorStateList.valueOf(
- (Integer) animation.getAnimatedValue())));
- animator.start();
- } else {
- QSIconViewImpl.setTint(mBg, circleColor);
- }
- mCircleColor = circleColor;
- }
-
- mShowRippleEffect = state.showRippleEffect;
- setClickable(state.state != Tile.STATE_UNAVAILABLE);
- setLongClickable(state.handlesLongClick);
- mIcon.setIcon(state, allowAnimations);
- setContentDescription(state.contentDescription);
- final StringBuilder stateDescription = new StringBuilder();
- String text = "";
- switch (state.state) {
- case Tile.STATE_UNAVAILABLE:
- text = mContext.getString(R.string.tile_unavailable);
- break;
- case Tile.STATE_INACTIVE:
- if (state instanceof QSTile.BooleanState) {
- text = mContext.getString(R.string.switch_bar_off);
- }
- break;
- case Tile.STATE_ACTIVE:
- if (state instanceof QSTile.BooleanState) {
- text = mContext.getString(R.string.switch_bar_on);
- }
- break;
- default:
- break;
- }
- if (!TextUtils.isEmpty(text)) {
- stateDescription.append(text);
- if (TextUtils.isEmpty(state.secondaryLabel)) {
- state.secondaryLabel = text;
- }
- }
- if (!TextUtils.isEmpty(state.stateDescription)) {
- stateDescription.append(", ");
- stateDescription.append(state.stateDescription);
- if (mLastState != INVALID && state.state == mLastState
- && !state.stateDescription.equals(mLastStateDescription)) {
- mStateDescriptionDeltas = state.stateDescription;
- }
- }
- setStateDescription(stateDescription.toString());
- mLastState = state.state;
- mLastStateDescription = state.stateDescription;
-
- mAccessibilityClass =
- state.state == Tile.STATE_UNAVAILABLE ? null : state.expandedAccessibilityClassName;
- if (state instanceof QSTile.BooleanState) {
- boolean newState = ((BooleanState) state).value;
- if (mTileState != newState) {
- mTileState = newState;
- }
- }
- }
-
- /* The view should not be animated if it's not on screen and no part of it is visible.
- */
- protected boolean animationsEnabled() {
- if (!isShown()) {
- return false;
- }
- if (getAlpha() != 1f) {
- return false;
- }
- getLocationOnScreen(mLocInScreen);
- return mLocInScreen[1] >= -getHeight();
- }
-
- protected int getCircleColor(int state) {
- switch (state) {
- case Tile.STATE_ACTIVE:
- return mColorActive;
- case Tile.STATE_INACTIVE:
- case Tile.STATE_UNAVAILABLE:
- return mColorInactive;
- default:
- Log.e(TAG, "Invalid state " + state);
- return 0;
- }
- }
-
- @Override
- public void setClickable(boolean clickable) {
- super.setClickable(clickable);
- setBackground(clickable && mShowRippleEffect ? mRipple : null);
- }
-
- @Override
- public int getDetailY() {
- return getTop() + getHeight() / 2;
- }
-
- public QSIconView getIcon() {
- return mIcon;
- }
-
- public View getIconWithBackground() {
- return mIconFrame;
- }
-
- @Override
- public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(event);
- if (!TextUtils.isEmpty(mAccessibilityClass)) {
- event.setClassName(mAccessibilityClass);
- }
- if (event.getContentChangeTypes() == CONTENT_CHANGE_TYPE_STATE_DESCRIPTION
- && mStateDescriptionDeltas != null) {
- event.getText().add(mStateDescriptionDeltas);
- mStateDescriptionDeltas = null;
- }
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(info);
- // Clear selected state so it is not announce by talkback.
- info.setSelected(false);
- if (!TextUtils.isEmpty(mAccessibilityClass)) {
- info.setClassName(mAccessibilityClass);
- if (Switch.class.getName().equals(mAccessibilityClass)) {
- String label = getResources().getString(
- mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
- // Set the text here for tests in
- // android.platform.test.scenario.sysui.quicksettings. Can be removed when
- // UiObject2 has a new getStateDescription() API and tests are updated.
- info.setText(label);
- info.setChecked(mTileState);
- info.setCheckable(true);
- if (isLongClickable()) {
- info.addAction(
- new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfo.AccessibilityAction
- .ACTION_LONG_CLICK.getId(),
- getResources().getString(
- R.string.accessibility_long_click_tile)));
- }
- }
- }
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
- sb.append("locInScreen=(" + mLocInScreen[0] + ", " + mLocInScreen[1] + ")");
- sb.append(", iconView=" + mIcon.toString());
- sb.append(", tileState=" + mTileState);
- sb.append("]");
- return sb.toString();
- }
-
- private class H extends Handler {
- private static final int STATE_CHANGED = 1;
-
- public H() {
- super(Looper.getMainLooper());
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == STATE_CHANGED) {
- handleStateChanged((QSTile.State) msg.obj);
- }
- }
- }
-} \ No newline at end of file
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 5ff785dc32d9..47d80bb0d16f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -38,7 +38,6 @@ import android.metrics.LogMaker;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.service.quicksettings.Tile;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -56,7 +55,6 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
-import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
@@ -563,21 +561,6 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
return mReadyState == READY_STATE_READY;
}
- public static int getColorForState(Context context, int state) {
- switch (state) {
- case Tile.STATE_UNAVAILABLE:
- return Utils.getDisabled(context,
- Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondary));
- case Tile.STATE_INACTIVE:
- return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondary);
- case Tile.STATE_ACTIVE:
- return Utils.getColorAttrDefaultColor(context, android.R.attr.colorPrimary);
- default:
- Log.e("QSTile", "Invalid state " + state);
- return 0;
- }
- }
-
protected final class H extends Handler {
private static final int ADD_CALLBACK = 1;
private static final int CLICK = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
deleted file mode 100644
index 50417e9ab259..000000000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2017 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.qs.tileimpl;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.service.quicksettings.Tile;
-import android.text.TextUtils;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.Nullable;
-
-import com.android.settingslib.Utils;
-import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSIconView;
-import com.android.systemui.plugins.qs.QSTile;
-
-import java.util.Objects;
-
-/** View that represents a standard quick settings tile. **/
-public class QSTileView extends QSTileBaseView {
- protected int mMaxLabelLines = 2;
- private View mDivider;
- protected TextView mLabel;
- protected TextView mSecondLine;
- private ImageView mPadLock;
- protected int mState;
- protected ButtonRelativeLayout mLabelContainer;
- private View mExpandIndicator;
- private View mExpandSpace;
- protected ColorStateList mColorLabelActive;
- protected ColorStateList mColorLabelInactive;
- private ColorStateList mColorLabelUnavailable;
- protected boolean mDualTargetAllowed = false;
-
- public QSTileView(Context context, QSIconView icon) {
- this(context, icon, false);
- }
-
- public QSTileView(Context context, QSIconView icon, boolean collapsedView) {
- super(context, icon, collapsedView);
-
- setClipChildren(false);
- setClipToPadding(false);
-
- setClickable(true);
- setId(View.generateViewId());
- createLabel();
- setOrientation(VERTICAL);
- setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
- mColorLabelActive = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary);
- mColorLabelInactive = mColorLabelActive;
- // The text color for unavailable tiles is textColorSecondary, same as secondaryLabel for
- // contrast purposes
- mColorLabelUnavailable = Utils.getColorAttr(getContext(),
- android.R.attr.textColorSecondary);
- }
-
- TextView getLabel() {
- return mLabel;
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- FontSizeUtils.updateFontSize(mLabel, R.dimen.qs_tile_text_size);
- FontSizeUtils.updateFontSize(mSecondLine, R.dimen.qs_tile_text_size);
- }
-
- @Override
- public int getDetailY() {
- return getTop() + mLabelContainer.getTop() + mLabelContainer.getHeight() / 2;
- }
-
- protected void createLabel() {
- mLabelContainer = (ButtonRelativeLayout) LayoutInflater.from(getContext())
- .inflate(R.layout.qs_tile_label, this, false);
- mLabelContainer.setClipChildren(false);
- mLabelContainer.setClipToPadding(false);
- mLabel = mLabelContainer.findViewById(R.id.tile_label);
- mPadLock = mLabelContainer.findViewById(R.id.restricted_padlock);
- mDivider = mLabelContainer.findViewById(R.id.underline);
- mExpandIndicator = mLabelContainer.findViewById(R.id.expand_indicator);
- mExpandSpace = mLabelContainer.findViewById(R.id.expand_space);
- mSecondLine = mLabelContainer.findViewById(R.id.app_label);
- addView(mLabelContainer);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mLabel.setSingleLine(false);
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- // Remeasure view if the primary label requires more than mMaxLabelLines lines or the
- // secondary label text will be cut off.
- if (shouldLabelBeSingleLine()) {
- mLabel.setSingleLine();
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- protected boolean shouldLabelBeSingleLine() {
- if (mCollapsedView) return true;
- if (mLabel.getLineCount() > mMaxLabelLines) {
- return true;
- } else if (!TextUtils.isEmpty(mSecondLine.getText())
- && mLabel.getLineCount() > mMaxLabelLines - 1) {
- return true;
- }
- return false;
- }
-
- @Override
- protected void handleStateChanged(QSTile.State state) {
- super.handleStateChanged(state);
- if (!Objects.equals(mLabel.getText(), state.label) || mState != state.state) {
- ColorStateList labelColor = getLabelColor(state.state);
- changeLabelColor(labelColor);
- mState = state.state;
- mLabel.setText(state.label);
- }
- if (!Objects.equals(mSecondLine.getText(), state.secondaryLabel)) {
- mSecondLine.setText(state.secondaryLabel);
- mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel)
- ? View.GONE : View.VISIBLE);
- }
- boolean dualTarget = mDualTargetAllowed && state.dualTarget;
- handleExpand(dualTarget);
- mLabelContainer.setContentDescription(dualTarget ? state.dualLabelContentDescription
- : null);
- if (dualTarget != mLabelContainer.isClickable()) {
- mLabelContainer.setClickable(dualTarget);
- mLabelContainer.setLongClickable(dualTarget);
- mLabelContainer.setBackground(dualTarget ? newTileBackground() : null);
- }
- mLabel.setEnabled(!state.disabledByPolicy);
- mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
- }
-
- protected final ColorStateList getLabelColor(int state) {
- if (state == Tile.STATE_ACTIVE) {
- return mColorLabelActive;
- } else if (state == Tile.STATE_INACTIVE) {
- return mColorLabelInactive;
- }
- return mColorLabelUnavailable;
- }
-
- protected void changeLabelColor(ColorStateList color) {
- mLabel.setTextColor(color);
- }
-
- protected void handleExpand(boolean dualTarget) {
- mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
- mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
- }
-
- @Override
- public void init(OnClickListener click, OnClickListener secondaryClick,
- OnLongClickListener longClick) {
- super.init(click, secondaryClick, longClick);
- mLabelContainer.setOnClickListener(secondaryClick);
- mLabelContainer.setOnLongClickListener(longClick);
- mLabelContainer.setClickable(false);
- mLabelContainer.setLongClickable(false);
- }
-
- public TextView getAppLabel() {
- return mSecondLine;
- }
-
- @Nullable
- @Override
- public View getLabelContainer() {
- return mLabelContainer;
- }
-
- @Override
- public View getSecondaryLabel() {
- return mSecondLine;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
deleted file mode 100644
index 70d51ee1c516..000000000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ /dev/null
@@ -1,215 +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.qs.tileimpl
-
-import android.animation.ValueAnimator
-import android.content.Context
-import android.content.res.ColorStateList
-import android.graphics.Color
-import android.graphics.drawable.Drawable
-import android.graphics.drawable.RippleDrawable
-import android.service.quicksettings.Tile.STATE_ACTIVE
-import android.view.Gravity
-import android.view.View
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import android.widget.ImageView
-import android.widget.LinearLayout
-import android.widget.RelativeLayout
-import com.android.systemui.R
-import com.android.systemui.plugins.qs.QSIconView
-import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState
-
-open class QSTileViewHorizontal(
- context: Context,
- icon: QSIconView,
- collapsed: Boolean
-) : QSTileView(context, icon, collapsed), HeightOverrideable {
-
- protected var colorBackgroundDrawable: Drawable? = null
- private var paintColor = Color.WHITE
- private var paintAnimator: ValueAnimator? = null
- private var labelAnimator: ValueAnimator? = null
- private var sideView: ImageView = ImageView(mContext)
- override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE
-
- init {
- orientation = HORIZONTAL
- gravity = Gravity.CENTER_VERTICAL or Gravity.START
- mDualTargetAllowed = false
- val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding)
- setPadding(padding, paddingTop, padding, paddingBottom)
-
- mBg.setImageDrawable(null)
- mIconFrame.removeAllViews()
- removeView(mIconFrame)
- val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
- addView(mIcon, 0, LayoutParams(iconSize, iconSize))
-
- sideView.visibility = View.GONE
- val sideViewLayoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
- gravity = Gravity.CENTER_VERTICAL
- marginStart = context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
- }
- addView(sideView, -1, sideViewLayoutParams)
- sideView.adjustViewBounds = true
- sideView.scaleType = ImageView.ScaleType.FIT_CENTER
-
- mColorLabelActive = ColorStateList.valueOf(getColorForState(getContext(), STATE_ACTIVE))
- changeLabelColor(getLabelColor(mState)) // Matches the default state of the tile
- }
-
- override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
- super.onLayout(changed, l, t, r, b)
- if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
- bottom = top + heightOverride
- }
- }
-
- override fun createLabel() {
- super.createLabel()
- findViewById<LinearLayout>(R.id.label_group)?.apply {
- gravity = Gravity.START
- (layoutParams as? RelativeLayout.LayoutParams)?.apply {
- removeRule(RelativeLayout.ALIGN_PARENT_TOP)
- }
- }
- mLabelContainer.setPadding(0, 0, 0, 0)
- (mLabelContainer.layoutParams as MarginLayoutParams).apply {
- marginStart = context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
- marginEnd = 0
- gravity = Gravity.CENTER_VERTICAL or Gravity.START
- }
- mLabel.gravity = Gravity.START
- mLabel.textDirection = TEXT_DIRECTION_LOCALE
- mSecondLine.gravity = Gravity.START
- mSecondLine.textDirection = TEXT_DIRECTION_LOCALE
-
- if (mCollapsedView) {
- mSecondLine.alpha = 0f
- mLabelContainer.setIgnoredView(mSecondLine)
- }
- }
-
- override fun shouldLabelBeSingleLine(): Boolean {
- return true
- }
-
- override fun updateRippleSize() {
- }
-
- override fun newTileBackground(): Drawable? {
- val ripple = mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
- colorBackgroundDrawable = ripple.findDrawableByLayerId(R.id.background)
- return ripple
- }
-
- override fun setClickable(clickable: Boolean) {
- super.setClickable(clickable)
- background = if (clickable && mShowRippleEffect) {
- mRipple?.also {
- // In case that the colorBackgroundDrawable was used as the background, make sure
- // it has the correct callback instead of null
- colorBackgroundDrawable?.callback = it
- }
- } else {
- colorBackgroundDrawable
- }
- }
-
- override fun handleStateChanged(state: QSTile.State) {
- super.handleStateChanged(state)
- mLabelContainer.background = null
-
- val allowAnimations = animationsEnabled() && paintColor != Color.WHITE
- val newColor = getCircleColor(state.state)
- if (allowAnimations) {
- animateBackground(newColor)
- } else {
- clearBackgroundAnimator()
- colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(newColor))?.also {
- paintColor = newColor
- }
- paintColor = newColor
- }
- loadSideViewDrawableIfNecessary(state)
- }
-
- private fun animateBackground(newBackgroundColor: Int) {
- if (newBackgroundColor != paintColor) {
- clearBackgroundAnimator()
- paintAnimator = ValueAnimator.ofArgb(paintColor, newBackgroundColor)
- .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
- addUpdateListener { animation: ValueAnimator ->
- val c = animation.animatedValue as Int
- colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(c))?.also {
- paintColor = c
- }
- }
- start()
- }
- }
- }
-
- override fun changeLabelColor(color: ColorStateList) {
- val allowAnimations = animationsEnabled()
- val currentColor = mLabel.textColors.defaultColor
- if (currentColor != color.defaultColor) {
- clearLabelAnimator()
- if (allowAnimations) {
- labelAnimator = ValueAnimator.ofArgb(currentColor, color.defaultColor)
- .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
- addUpdateListener {
- setLabelsColor(ColorStateList.valueOf(it.animatedValue as Int))
- }
- start()
- }
- } else {
- setLabelsColor(color)
- }
- }
- }
-
- private fun setLabelsColor(color: ColorStateList) {
- mLabel.setTextColor(color)
- mSecondLine.setTextColor(color)
- }
-
- private fun clearBackgroundAnimator() {
- paintAnimator?.cancel()?.also { paintAnimator = null }
- }
-
- private fun clearLabelAnimator() {
- labelAnimator?.cancel()?.also { labelAnimator = null }
- }
-
- private fun loadSideViewDrawableIfNecessary(state: QSTile.State) {
- if (state.sideViewDrawable != null) {
- sideView.setImageDrawable(state.sideViewDrawable)
- sideView.visibility = View.VISIBLE
- } else {
- sideView.setImageDrawable(null)
- sideView.visibility = GONE
- }
- }
-
- override fun handleExpand(dualTarget: Boolean) {}
-
- override fun getSecondaryIcon(): View {
- return sideView
- }
-} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
new file mode 100644
index 000000000000..3ad95d27e940
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2021 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.qs.tileimpl
+
+import android.animation.ValueAnimator
+import android.content.Context
+import android.content.res.ColorStateList
+import android.content.res.Configuration
+import android.content.res.Resources.ID_NULL
+import android.graphics.Color
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.RippleDrawable
+import android.service.quicksettings.Tile
+import android.text.TextUtils
+import android.util.Log
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityNodeInfo
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.Switch
+import android.widget.TextView
+import com.android.settingslib.Utils
+import com.android.systemui.FontSizeUtils
+import com.android.systemui.R
+import com.android.systemui.plugins.qs.QSIconView
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.qs.QSTile.BooleanState
+import com.android.systemui.plugins.qs.QSTileView
+import java.util.Objects
+
+private const val TAG = "QSTileViewImpl"
+open class QSTileViewImpl @JvmOverloads constructor(
+ context: Context,
+ private val _icon: QSIconView,
+ private val collapsed: Boolean = false
+) : QSTileView(context), HeightOverrideable {
+
+ companion object {
+ private const val INVALID = -1
+ }
+
+ override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE
+
+ private val colorActive = Utils.getColorAttrDefaultColor(context,
+ com.android.internal.R.attr.colorAccentPrimary)
+ private val colorInactive = Utils.getColorAttrDefaultColor(context, R.attr.offStateColor)
+ private val colorUnavailable =
+ Utils.getColorAttrDefaultColor(context, android.R.attr.colorBackground)
+
+ private val colorLabelActive =
+ Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimaryInverse)
+ private val colorLabelInactive =
+ Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
+ private val colorLabelUnavailable =
+ Utils.getColorAttrDefaultColor(context, android.R.attr.textColorTertiary)
+
+ private lateinit var label: TextView
+ protected lateinit var secondaryLabel: TextView
+ private lateinit var labelContainer: IgnorableChildLinearLayout
+ protected lateinit var sideView: ViewGroup
+ private lateinit var customDrawableView: ImageView
+ private lateinit var chevronView: ImageView
+
+ protected var showRippleEffect = true
+
+ private lateinit var ripple: RippleDrawable
+ private lateinit var colorBackgroundDrawable: Drawable
+ private var paintColor = Color.WHITE
+ private var paintAnimator: ValueAnimator? = null
+ private var labelAnimator: ValueAnimator? = null
+ private var secondaryLabelAnimator: ValueAnimator? = null
+
+ private var accessibilityClass: String? = null
+ private var stateDescriptionDeltas: CharSequence? = null
+ private var lastStateDescription: CharSequence? = null
+ private var tileState = false
+ private var lastState = INVALID
+
+ private val locInScreen = IntArray(2)
+
+ init {
+ setId(generateViewId())
+ orientation = LinearLayout.HORIZONTAL
+ gravity = Gravity.CENTER_VERTICAL or Gravity.START
+ importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_YES
+ clipChildren = false
+ clipToPadding = false
+ isFocusable = true
+ background = createTileBackground()
+
+ val padding = resources.getDimensionPixelSize(R.dimen.qs_tile_padding)
+ val startPadding = resources.getDimensionPixelSize(R.dimen.qs_tile_start_padding)
+ setPaddingRelative(startPadding, padding, padding, padding)
+
+ val iconSize = resources.getDimensionPixelSize(R.dimen.qs_icon_size)
+ addView(_icon, LayoutParams(iconSize, iconSize))
+
+ createAndAddLabels()
+ createAndAddSideView()
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration?) {
+ super.onConfigurationChanged(newConfig)
+ updateResources()
+ }
+
+ fun updateResources() {
+ FontSizeUtils.updateFontSize(label, R.dimen.qs_tile_text_size)
+ FontSizeUtils.updateFontSize(secondaryLabel, R.dimen.qs_tile_text_size)
+
+ val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
+ _icon.layoutParams.apply {
+ height = iconSize
+ width = iconSize
+ }
+
+ val padding = resources.getDimensionPixelSize(R.dimen.qs_tile_padding)
+ val startPadding = resources.getDimensionPixelSize(R.dimen.qs_tile_start_padding)
+ setPaddingRelative(startPadding, padding, padding, padding)
+
+ val labelMargin = resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
+ (labelContainer.layoutParams as MarginLayoutParams).apply {
+ marginStart = labelMargin
+ }
+
+ (sideView.layoutParams as MarginLayoutParams).apply {
+ marginStart = labelMargin
+ }
+ (chevronView.layoutParams as MarginLayoutParams).apply {
+ height = iconSize
+ width = iconSize
+ }
+
+ val endMargin = resources.getDimensionPixelSize(R.dimen.qs_drawable_end_margin)
+ (customDrawableView.layoutParams as MarginLayoutParams).apply {
+ height = iconSize
+ marginEnd = endMargin
+ }
+ }
+
+ private fun createAndAddLabels() {
+ labelContainer = LayoutInflater.from(context)
+ .inflate(R.layout.qs_tile_label, this, false) as IgnorableChildLinearLayout
+ label = labelContainer.requireViewById(R.id.tile_label)
+ secondaryLabel = labelContainer.requireViewById(R.id.app_label)
+ if (collapsed) {
+ labelContainer.ignoreLastView = true
+ secondaryLabel.alpha = 0f
+ }
+ label.setTextColor(getLabelColor(0)) // Default state
+ secondaryLabel.setTextColor(getSecondaryLabelColor(0))
+ addView(labelContainer)
+ }
+
+ private fun createAndAddSideView() {
+ sideView = LayoutInflater.from(context)
+ .inflate(R.layout.qs_tile_side_icon, this, false) as ViewGroup
+ customDrawableView = sideView.requireViewById(R.id.customDrawable)
+ chevronView = sideView.requireViewById(R.id.chevron)
+ addView(sideView)
+ }
+
+ fun createTileBackground(): Drawable {
+ ripple = mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
+ colorBackgroundDrawable = ripple.findDrawableByLayerId(R.id.background)
+ return ripple
+ }
+
+ override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
+ super.onLayout(changed, l, t, r, b)
+ if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
+ bottom = top + heightOverride
+ }
+ }
+
+ override fun updateAccessibilityOrder(previousView: View?): View {
+ accessibilityTraversalAfter = previousView?.id ?: ID_NULL
+ return this
+ }
+
+ override fun getIcon(): QSIconView {
+ return _icon
+ }
+
+ override fun getIconWithBackground(): View {
+ return icon
+ }
+
+ override fun init(tile: QSTile) {
+ init(
+ { v: View? -> tile.click(this) },
+ { view: View? ->
+ tile.longClick(this)
+ true
+ }
+ )
+ }
+
+ private fun init(
+ click: OnClickListener?,
+ longClick: OnLongClickListener?
+ ) {
+ setOnClickListener(click)
+ onLongClickListener = longClick
+ }
+
+ override fun onStateChanged(state: QSTile.State) {
+ post {
+ handleStateChanged(state)
+ }
+ }
+
+ override fun getDetailY(): Int {
+ return top + height / 2
+ }
+
+ override fun hasOverlappingRendering(): Boolean {
+ // Avoid layers for this layout - we don't need them.
+ return false
+ }
+
+ override fun setClickable(clickable: Boolean) {
+ super.setClickable(clickable)
+ background = if (clickable && showRippleEffect) {
+ ripple.also {
+ // In case that the colorBackgroundDrawable was used as the background, make sure
+ // it has the correct callback instead of null
+ colorBackgroundDrawable.callback = it
+ }
+ } else {
+ colorBackgroundDrawable
+ }
+ }
+
+ override fun getLabelContainer(): View {
+ return labelContainer
+ }
+
+ override fun getSecondaryLabel(): View {
+ return secondaryLabel
+ }
+
+ override fun getSecondaryIcon(): View {
+ return sideView
+ }
+
+ // Accessibility
+
+ override fun onInitializeAccessibilityEvent(event: AccessibilityEvent) {
+ super.onInitializeAccessibilityEvent(event)
+ if (!TextUtils.isEmpty(accessibilityClass)) {
+ event.className = accessibilityClass
+ }
+ if (event.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION &&
+ stateDescriptionDeltas != null) {
+ event.text.add(stateDescriptionDeltas)
+ stateDescriptionDeltas = null
+ }
+ }
+
+ override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) {
+ super.onInitializeAccessibilityNodeInfo(info)
+ // Clear selected state so it is not announce by talkback.
+ info.isSelected = false
+ if (!TextUtils.isEmpty(accessibilityClass)) {
+ info.className = accessibilityClass
+ if (Switch::class.java.name == accessibilityClass) {
+ val label = resources.getString(
+ if (tileState) R.string.switch_bar_on else R.string.switch_bar_off)
+ // Set the text here for tests in
+ // android.platform.test.scenario.sysui.quicksettings. Can be removed when
+ // UiObject2 has a new getStateDescription() API and tests are updated.
+ info.text = label
+ info.isChecked = tileState
+ info.isCheckable = true
+ if (isLongClickable) {
+ info.addAction(
+ AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
+ resources.getString(
+ R.string.accessibility_long_click_tile)))
+ }
+ }
+ }
+ }
+
+ override fun toString(): String {
+ val sb = StringBuilder(javaClass.simpleName).append('[')
+ sb.append("locInScreen=(${locInScreen[0]}, ${locInScreen[1]})")
+ sb.append(", iconView=$_icon")
+ sb.append(", tileState=$tileState")
+ sb.append("]")
+ return sb.toString()
+ }
+
+ // HANDLE STATE CHANGES RELATED METHODS
+
+ protected open fun handleStateChanged(state: QSTile.State) {
+ val allowAnimations = animationsEnabled() && paintColor != Color.WHITE
+ showRippleEffect = state.showRippleEffect
+ isClickable = state.state != Tile.STATE_UNAVAILABLE
+ isLongClickable = state.handlesLongClick
+ icon.setIcon(state, allowAnimations)
+ contentDescription = state.contentDescription
+
+ // Background color animation
+ val newColor = getCircleColor(state.state)
+ if (allowAnimations) {
+ animateBackground(newColor)
+ } else {
+ clearBackgroundAnimator()
+ colorBackgroundDrawable.setTintList(ColorStateList.valueOf(newColor)).also {
+ paintColor = newColor
+ }
+ paintColor = newColor
+ }
+ //
+
+ // State handling and description
+ val stateDescription = StringBuilder()
+ val stateText = getStateText(state)
+ if (!TextUtils.isEmpty(stateText)) {
+ stateDescription.append(stateText)
+ if (TextUtils.isEmpty(state.secondaryLabel)) {
+ state.secondaryLabel = stateText
+ }
+ }
+ if (!TextUtils.isEmpty(state.stateDescription)) {
+ stateDescription.append(", ")
+ stateDescription.append(state.stateDescription)
+ if (lastState != INVALID && state.state == lastState &&
+ state.stateDescription != lastStateDescription) {
+ stateDescriptionDeltas = state.stateDescription
+ }
+ }
+
+ setStateDescription(stateDescription.toString())
+ lastStateDescription = state.stateDescription
+
+ accessibilityClass = if (state.state == Tile.STATE_UNAVAILABLE) {
+ null
+ } else {
+ state.expandedAccessibilityClassName
+ }
+
+ if (state is BooleanState) {
+ val newState = state.value
+ if (tileState != newState) {
+ tileState = newState
+ }
+ }
+ //
+
+ // Labels
+ if (!Objects.equals(label.text, state.label)) {
+ label.text = state.label
+ }
+ if (!Objects.equals(secondaryLabel.text, state.secondaryLabel)) {
+ secondaryLabel.text = state.secondaryLabel
+ secondaryLabel.visibility = if (TextUtils.isEmpty(state.secondaryLabel)) {
+ GONE
+ } else {
+ VISIBLE
+ }
+ }
+
+ if (allowAnimations) {
+ animateLabelColor(getLabelColor(state.state))
+ animateSecondaryLabelColor(getSecondaryLabelColor(state.state))
+ } else {
+ label.setTextColor(getLabelColor(state.state))
+ secondaryLabel.setTextColor(getSecondaryLabelColor(state.state))
+ }
+
+ // Right side icon
+ loadSideViewDrawableIfNecessary(state)
+ chevronView.imageTintList = ColorStateList.valueOf(getSecondaryLabelColor(state.state))
+
+ label.isEnabled = !state.disabledByPolicy
+
+ lastState = state.state
+ }
+
+ private fun loadSideViewDrawableIfNecessary(state: QSTile.State) {
+ if (state.sideViewCustomDrawable != null) {
+ customDrawableView.setImageDrawable(state.sideViewCustomDrawable)
+ customDrawableView.visibility = VISIBLE
+ chevronView.visibility = GONE
+ } else if (state !is BooleanState || state.forceExpandIcon) {
+ customDrawableView.setImageDrawable(null)
+ customDrawableView.visibility = GONE
+ chevronView.visibility = VISIBLE
+ } else {
+ customDrawableView.setImageDrawable(null)
+ customDrawableView.visibility = GONE
+ chevronView.visibility = GONE
+ }
+ }
+
+ private fun getStateText(state: QSTile.State): String {
+ return if (state.disabledByPolicy) {
+ context.getString(R.string.tile_disabled)
+ } else if (state.state == Tile.STATE_UNAVAILABLE) {
+ context.getString(R.string.tile_unavailable)
+ } else if (state is BooleanState) {
+ if (state.state == Tile.STATE_INACTIVE) {
+ context.getString(R.string.switch_bar_off)
+ } else {
+ context.getString(R.string.switch_bar_on)
+ }
+ } else {
+ ""
+ }
+ }
+
+ /*
+ * The view should not be animated if it's not on screen and no part of it is visible.
+ */
+ protected open fun animationsEnabled(): Boolean {
+ if (!isShown) {
+ return false
+ }
+ if (alpha != 1f) {
+ return false
+ }
+ getLocationOnScreen(locInScreen)
+ return locInScreen.get(1) >= -height
+ }
+
+ private fun animateBackground(newBackgroundColor: Int) {
+ if (newBackgroundColor != paintColor) {
+ clearBackgroundAnimator()
+ paintAnimator = ValueAnimator.ofArgb(paintColor, newBackgroundColor)
+ .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
+ addUpdateListener { animation: ValueAnimator ->
+ val c = animation.animatedValue as Int
+ colorBackgroundDrawable.setTintList(ColorStateList.valueOf(c)).also {
+ paintColor = c
+ }
+ }
+ start()
+ }
+ }
+ }
+
+ private fun animateLabelColor(color: Int) {
+ val currentColor = label.textColors.defaultColor
+ if (currentColor != color) {
+ clearLabelAnimator()
+ labelAnimator = ValueAnimator.ofArgb(currentColor, color)
+ .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
+ addUpdateListener {
+ label.setTextColor(it.animatedValue as Int)
+ }
+ start()
+ }
+ }
+ }
+
+ private fun animateSecondaryLabelColor(color: Int) {
+ val currentColor = secondaryLabel.textColors.defaultColor
+ if (currentColor != color) {
+ clearSecondaryLabelAnimator()
+ secondaryLabelAnimator = ValueAnimator.ofArgb(currentColor, color)
+ .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
+ addUpdateListener {
+ secondaryLabel.setTextColor(it.animatedValue as Int)
+ }
+ start()
+ }
+ }
+ }
+
+ private fun clearBackgroundAnimator() {
+ paintAnimator?.cancel()?.also { paintAnimator = null }
+ }
+
+ private fun clearLabelAnimator() {
+ labelAnimator?.cancel()?.also { labelAnimator = null }
+ }
+
+ private fun clearSecondaryLabelAnimator() {
+ secondaryLabelAnimator?.cancel()?.also { secondaryLabelAnimator = null }
+ }
+
+ private fun getCircleColor(state: Int): Int {
+ return when (state) {
+ Tile.STATE_ACTIVE -> colorActive
+ Tile.STATE_INACTIVE -> colorInactive
+ Tile.STATE_UNAVAILABLE -> colorUnavailable
+ else -> {
+ Log.e(TAG, "Invalid state $state")
+ 0
+ }
+ }
+ }
+
+ private fun getLabelColor(state: Int): Int {
+ return when (state) {
+ Tile.STATE_ACTIVE -> colorLabelActive
+ Tile.STATE_INACTIVE -> colorLabelInactive
+ Tile.STATE_UNAVAILABLE -> colorLabelUnavailable
+ else -> {
+ Log.e(TAG, "Invalid state $state")
+ 0
+ }
+ }
+ }
+
+ private fun getSecondaryLabelColor(state: Int): Int {
+ return when (state) {
+ Tile.STATE_ACTIVE -> colorLabelActive
+ Tile.STATE_INACTIVE, Tile.STATE_UNAVAILABLE -> colorLabelUnavailable
+ else -> {
+ Log.e(TAG, "Invalid state $state")
+ 0
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 5dcbf49a9b72..577c0d8455eb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -99,7 +99,9 @@ public class InternetTile extends QSTileImpl<SignalState> {
@Override
public SignalState newTileState() {
- return new SignalState();
+ SignalState s = new SignalState();
+ s.forceExpandIcon = true;
+ return s;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index d1b74cd7a505..85b835ae7ac9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -174,7 +174,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
} else {
state.state = Tile.STATE_UNAVAILABLE;
}
- state.sideViewDrawable = isDeviceLocked ? null : mCardViewDrawable;
+ state.sideViewCustomDrawable = isDeviceLocked ? null : mCardViewDrawable;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index ef2c1c903a85..7bde64b3e761 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -129,6 +129,8 @@ public class WorkModeTile extends QSTileImpl<BooleanState> implements
state.contentDescription = state.label;
state.expandedAccessibilityClassName = Switch.class.getName();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+ state.secondaryLabel = state.value ? "" :
+ mContext.getString(R.string.quick_settings_work_mode_paused);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index b3f7ca6f2630..eb3a17f4ccce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -181,12 +181,19 @@ class PrivacyDotViewController @Inject constructor(
designatedCorner = newCorner
if (animationScheduler.hasPersistentDot) {
- designatedCorner!!.visibility = View.VISIBLE
- designatedCorner!!.alpha = 0f
- designatedCorner!!.animate()
- .alpha(1.0f)
- .setDuration(300)
- .start()
+ fadeInDot()
+ }
+ }
+
+ @UiThread
+ private fun fadeInDot() {
+ designatedCorner?.let { dot ->
+ dot.visibility = View.VISIBLE
+ dot.alpha = 0f
+ dot.animate()
+ .alpha(1.0f)
+ .setDuration(300)
+ .start()
}
}
@@ -300,9 +307,14 @@ class PrivacyDotViewController @Inject constructor(
private val systemStatusAnimationCallback: SystemStatusAnimationCallback =
object : SystemStatusAnimationCallback {
- override fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? {
+ override fun onSystemStatusAnimationTransitionToPersistentDot(
+ showAnimation: Boolean
+ ): Animator? {
if (designatedCorner == null) {
return null
+ } else if (!showAnimation) {
+ uiExecutor?.execute { fadeInDot() }
+ return null
}
val alpha = ObjectAnimator.ofFloat(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
index 398f5e35e549..539020d52db5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
@@ -31,6 +31,8 @@ interface StatusEvent {
val priority: Int
// Whether or not to force the status bar open and show a dot
val forceVisible: Boolean
+ // Whether or not to show an animation for this event
+ val showAnimation: Boolean
val viewCreator: (context: Context) -> View
// Update this event with values from another event.
@@ -47,6 +49,7 @@ interface StatusEvent {
class BatteryEvent : StatusEvent {
override val priority = 50
override val forceVisible = false
+ override val showAnimation = true
override val viewCreator: (context: Context) -> View = { context ->
val iv = ImageView(context)
@@ -59,7 +62,7 @@ class BatteryEvent : StatusEvent {
return javaClass.simpleName
}
}
-class PrivacyEvent : StatusEvent {
+class PrivacyEvent(override val showAnimation: Boolean = true) : StatusEvent {
override val priority = 100
override val forceVisible = true
var privacyItems: List<PrivacyItem> = listOf()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
index bde085e4e0b8..ba50659f5567 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
@@ -16,10 +16,13 @@
package com.android.systemui.statusbar.events
+import android.provider.DeviceConfig
+import android.provider.DeviceConfig.NAMESPACE_PRIVACY
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.privacy.PrivacyItem
import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
/**
@@ -28,6 +31,7 @@ import javax.inject.Inject
*/
@SysUISingleton
class SystemEventCoordinator @Inject constructor(
+ private val systemClock: SystemClock,
private val batteryController: BatteryController,
private val privacyController: PrivacyItemController
) {
@@ -59,8 +63,8 @@ class SystemEventCoordinator @Inject constructor(
scheduler.setShouldShowPersistentPrivacyIndicator(false)
}
- fun notifyPrivacyItemsChanged() {
- val event = PrivacyEvent()
+ fun notifyPrivacyItemsChanged(showAnimation: Boolean = true) {
+ val event = PrivacyEvent(showAnimation)
event.privacyItems = privacyStateListener.currentPrivacyItems
scheduler.onStatusEvent(event)
}
@@ -90,8 +94,17 @@ class SystemEventCoordinator @Inject constructor(
private val privacyStateListener = object : PrivacyItemController.Callback {
var currentPrivacyItems = listOf<PrivacyItem>()
+ var previousPrivacyItems = listOf<PrivacyItem>()
+ var timeLastEmpty = systemClock.elapsedRealtime()
override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
+ if (uniqueItemsMatch(privacyItems, currentPrivacyItems)) {
+ return
+ } else if (privacyItems.isEmpty()) {
+ previousPrivacyItems = currentPrivacyItems
+ timeLastEmpty = systemClock.elapsedRealtime()
+ }
+
currentPrivacyItems = privacyItems
notifyListeners()
}
@@ -100,10 +113,25 @@ class SystemEventCoordinator @Inject constructor(
if (currentPrivacyItems.isEmpty()) {
notifyPrivacyItemsEmpty()
} else {
- notifyPrivacyItemsChanged()
+ val showAnimation = isChipAnimationEnabled() &&
+ (!uniqueItemsMatch(currentPrivacyItems, previousPrivacyItems) ||
+ systemClock.elapsedRealtime() - timeLastEmpty >= DEBOUNCE_TIME)
+ notifyPrivacyItemsChanged(showAnimation)
}
}
+
+ // Return true if the lists contain the same permission groups, used by the same UIDs
+ private fun uniqueItemsMatch(one: List<PrivacyItem>, two: List<PrivacyItem>): Boolean {
+ return one.map { it.application.uid to it.privacyType.permGroupName }.toSet() ==
+ two.map { it.application.uid to it.privacyType.permGroupName }.toSet()
+ }
+
+ private fun isChipAnimationEnabled(): Boolean {
+ return DeviceConfig.getBoolean(NAMESPACE_PRIVACY, CHIP_ANIMATION_ENABLED, true)
+ }
}
}
+private const val DEBOUNCE_TIME = 3000L
+private const val CHIP_ANIMATION_ENABLED = "privacy_chip_animation_enabled"
private const val TAG = "SystemEventCoordinator" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index 8da7fda242c7..b6f041685dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -103,7 +103,12 @@ class SystemStatusAnimationScheduler @Inject constructor(
if (DEBUG) {
Log.d(TAG, "scheduling event $event")
}
- scheduleEvent(event)
+ if (event.showAnimation) {
+ scheduleEvent(event)
+ } else if (event.forceVisible) {
+ hasPersistentDot = true
+ notifyTransitionToPersistentDot(showAnimation = false)
+ }
} else {
if (DEBUG) {
Log.d(TAG, "ignoring event $event")
@@ -197,7 +202,7 @@ class SystemStatusAnimationScheduler @Inject constructor(
aSet2.play(chipAnimator).before(systemAnimator)
if (hasPersistentDot) {
- val dotAnim = notifyTransitionToPersistentDot()
+ val dotAnim = notifyTransitionToPersistentDot(showAnimation = true)
if (dotAnim != null) aSet2.playTogether(systemAnimator, dotAnim)
}
@@ -209,9 +214,9 @@ class SystemStatusAnimationScheduler @Inject constructor(
}, DELAY)
}
- private fun notifyTransitionToPersistentDot(): Animator? {
+ private fun notifyTransitionToPersistentDot(showAnimation: Boolean): Animator? {
val anims: List<Animator> = listeners.mapNotNull {
- it.onSystemStatusAnimationTransitionToPersistentDot()
+ it.onSystemStatusAnimationTransitionToPersistentDot(showAnimation)
}
if (anims.isNotEmpty()) {
val aSet = AnimatorSet()
@@ -321,7 +326,9 @@ interface SystemStatusAnimationCallback {
@JvmDefault fun onSystemChromeAnimationEnd() {}
// Best method name, change my mind
- @JvmDefault fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? { return null }
+ @JvmDefault fun onSystemStatusAnimationTransitionToPersistentDot(
+ showAnimation: Boolean
+ ): Animator? { return null }
@JvmDefault fun onHidePersistentDot(): Animator? { return null }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index ae68462417a7..c911e3d111cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -220,17 +220,22 @@ public class KeyguardBouncer {
*/
private void onFullyHidden() {
cancelShowRunnable();
- if (mRoot != null) {
- mRoot.setVisibility(View.INVISIBLE);
- }
+ setVisibility(View.INVISIBLE);
mFalsingCollector.onBouncerHidden();
DejankUtils.postAfterTraversal(mResetRunnable);
}
+ private void setVisibility(@View.Visibility int visibility) {
+ if (mRoot != null) {
+ mRoot.setVisibility(visibility);
+ dispatchVisibilityChanged();
+ }
+ }
+
private final Runnable mShowRunnable = new Runnable() {
@Override
public void run() {
- mRoot.setVisibility(View.VISIBLE);
+ setVisibility(View.VISIBLE);
showPromptReason(mBouncerPromptReason);
final CharSequence customMessage = mCallback.consumeCustomMessage();
if (customMessage != null) {
@@ -299,7 +304,7 @@ public class KeyguardBouncer {
}
mIsAnimatingAway = false;
if (mRoot != null) {
- mRoot.setVisibility(View.INVISIBLE);
+ setVisibility(View.INVISIBLE);
if (destroyView) {
// We have a ViewFlipper that unregisters a broadcast when being detached, which may
@@ -436,7 +441,7 @@ public class KeyguardBouncer {
mContainer.addView(mRoot, mContainer.getChildCount());
mStatusBarHeight = mRoot.getResources().getDimensionPixelOffset(
com.android.systemui.R.dimen.status_bar_height);
- mRoot.setVisibility(View.INVISIBLE);
+ setVisibility(View.INVISIBLE);
final WindowInsets rootInsets = mRoot.getRootWindowInsets();
if (rootInsets != null) {
@@ -533,6 +538,12 @@ public class KeyguardBouncer {
}
}
+ private void dispatchVisibilityChanged() {
+ for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ callback.onVisibilityChanged(mRoot.getVisibility() == View.VISIBLE);
+ }
+ }
+
/**
* Apply keyguard configuration from the currently active resources. This can be called when the
* device configuration changes, to re-apply some resources that are qualified on the device
@@ -573,6 +584,13 @@ public class KeyguardBouncer {
* to 1f {@link KeyguardBouncer#EXPANSION_HIDDEN} when fully hidden
*/
default void onExpansionChanged(float bouncerHideAmount) {}
+
+ /**
+ * Invoked when visibility of KeyguardBouncer has changed.
+ * Note the bouncer expansion can be {@link KeyguardBouncer#EXPANSION_VISIBLE}, but the
+ * view's visibility can be {@link View.INVISIBLE}.
+ */
+ default void onVisibilityChanged(boolean isVisible) {}
}
/** Create a {@link KeyguardBouncer} once a container and bouncer callback are available. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index 5085e1cf9990..d84bb908fe69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -125,6 +125,7 @@ public class KeyguardIndicationTextView extends TextView {
if (info != null) {
setTextColor(info.getTextColor());
setOnClickListener(info.getClickListener());
+ setClickable(info.getClickListener() != null);
final Drawable icon = info.getIcon();
if (icon != null) {
icon.setTint(getCurrentTextColor());
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 1ef8470180f3..6ed375e708e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -129,6 +129,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
updateStates();
}
+
+ @Override
+ public void onVisibilityChanged(boolean isVisible) {
+ if (mAlternateAuthInterceptor != null) {
+ mAlternateAuthInterceptor.onBouncerVisibilityChanged();
+ }
+ }
};
private final DockManager.DockEventListener mDockEventListener =
new DockManager.DockEventListener() {
@@ -1126,6 +1133,18 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
/**
+ * Request to show the udfps affordance in a particular color. This can be used if an
+ * occluding app on the keyguard would like to request udfps. This method does nothing if
+ * {@link KeyguardUpdateMonitor#shouldListenForFingerprint} is false.
+ */
+ public void requestUdfps(boolean request, int color) {
+ if (mAlternateAuthInterceptor == null) {
+ return;
+ }
+ mAlternateAuthInterceptor.requestUdfps(request, color);
+ }
+
+ /**
* Delegate used to send show/reset events to an alternate authentication method instead of the
* regular pin/pattern/password bouncer.
*/
@@ -1174,5 +1193,19 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
*/
void setBouncerExpansionChanged(float expansion);
+ /**
+ * called when the bouncer view visibility has changed.
+ */
+ void onBouncerVisibilityChanged();
+
+ /**
+ * Use when an app occluding the keyguard would like to give the user ability to
+ * unlock the device using udfps.
+ *
+ * @param color of the udfps icon. should have proper contrast with its background. only
+ * used if requestUdfps = true
+ */
+ void requestUdfps(boolean requestUdfps, int color);
+
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
new file mode 100644
index 000000000000..1fe77fd441bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 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.ongoingcall
+
+import android.content.Context
+import android.util.AttributeSet
+
+import android.widget.Chronometer
+
+/**
+ * A [Chronometer] specifically for the ongoing call chip in the status bar.
+ *
+ * This class handles:
+ * 1) Setting the text width. If we used a basic WRAP_CONTENT for width, the chip width would
+ * change slightly each second because the width of each number is slightly different.
+ *
+ * Instead, we save the largest number width seen so far and ensure that the chip is at least
+ * that wide. This means the chip may get larger over time (e.g. in the transition from 59:59
+ * to 1:00:00), but never smaller.
+ *
+ * 2) Hiding the text if the time gets too long for the space available. Once the text has been
+ * hidden, it remains hidden for the duration of the call.
+ *
+ * Note that if the text was too big in portrait mode, resulting in the text being hidden, then the
+ * text will also be hidden in landscape (even if there is enough space for it in landscape).
+ */
+class OngoingCallChronometer @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyle: Int = 0
+) : Chronometer(context, attrs, defStyle) {
+
+ // Minimum width that the text view can be. Corresponds with the largest number width seen so
+ // far.
+ var minimumTextWidth: Int = 0
+
+ // True if the text is too long for the space available, so the text should be hidden.
+ var shouldHideText: Boolean = false
+
+ override fun setBase(base: Long) {
+ // These variables may have changed during the previous call, so re-set them before the new
+ // call starts.
+ minimumTextWidth = 0
+ shouldHideText = false
+ super.setBase(base)
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ if (shouldHideText) {
+ setMeasuredDimension(0, 0)
+ return
+ }
+
+ // Evaluate how wide the text *wants* to be if it had unlimited space.
+ super.onMeasure(
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+ heightMeasureSpec)
+ val desiredTextWidth = measuredWidth
+
+ // Evaluate how wide the text *can* be based on the enforced constraints
+ val enforcedTextWidth = resolveSize(desiredTextWidth, widthMeasureSpec)
+
+ if (desiredTextWidth > enforcedTextWidth) {
+ shouldHideText = true
+ setMeasuredDimension(0, 0)
+ } else {
+ // It's possible that the current text could fit in a smaller width, but we don't want
+ // the chip to change size every second. Instead, keep it at the minimum required width.
+ minimumTextWidth = desiredTextWidth.coerceAtLeast(minimumTextWidth)
+ setMeasuredDimension(minimumTextWidth, MeasureSpec.getSize(heightMeasureSpec))
+ }
+ }
+}
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 08cdebd5d80a..edea3055a783 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -58,6 +58,7 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import java.io.FileDescriptor;
@@ -296,7 +297,7 @@ public class GarbageMonitor implements Dumpable {
MemoryIconDrawable(Context context) {
baseIcon = context.getDrawable(R.drawable.ic_memory).mutate();
dp = context.getResources().getDisplayMetrics().density;
- paint.setColor(QSTileImpl.getColorForState(context, STATE_ACTIVE));
+ paint.setColor(QSIconViewImpl.getIconColorForState(context, STATE_ACTIVE));
}
public void setRss(long rss) {
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 92e68f36d491..ac8b16a2206d 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -16,6 +16,7 @@
package com.android.systemui.wallet.ui;
+import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.service.quickaccesswallet.QuickAccessWalletClient;
@@ -34,6 +35,7 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.LifecycleActivity;
@@ -54,6 +56,7 @@ public class WalletActivity extends LifecycleActivity {
private final Handler mHandler;
private final FalsingManager mFalsingManager;
private final UserTracker mUserTracker;
+ private final StatusBarKeyguardViewManager mKeyguardViewManager;
private WalletScreenController mWalletScreenController;
@Inject
@@ -65,7 +68,8 @@ public class WalletActivity extends LifecycleActivity {
@Background Executor executor,
@Main Handler handler,
FalsingManager falsingManager,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ StatusBarKeyguardViewManager keyguardViewManager) {
mQuickAccessWalletClient = quickAccessWalletClient;
mKeyguardStateController = keyguardStateController;
mKeyguardDismissUtil = keyguardDismissUtil;
@@ -74,6 +78,7 @@ public class WalletActivity extends LifecycleActivity {
mHandler = handler;
mFalsingManager = falsingManager;
mUserTracker = userTracker;
+ mKeyguardViewManager = keyguardViewManager;
}
@Override
@@ -136,6 +141,13 @@ public class WalletActivity extends LifecycleActivity {
protected void onResume() {
super.onResume();
mWalletScreenController.queryWalletCards();
+ mKeyguardViewManager.requestUdfps(true, Color.BLACK);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mKeyguardViewManager.requestUdfps(false, -1);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java
index 4200241603ea..1e1b459382d7 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java
@@ -61,7 +61,7 @@ public class WalletCardCarousel extends RecyclerView {
static final int CARD_ANIM_ALPHA_DELAY = 50;
private final Rect mSystemGestureExclusionZone = new Rect();
- private WalletCardCarouselAdapter mWalletCardCarouselAdapter;
+ private final WalletCardCarouselAdapter mWalletCardCarouselAdapter;
private int mExpectedViewWidth;
private int mCardMarginPx;
private int mCardWidthPx;
@@ -79,12 +79,6 @@ public class WalletCardCarousel extends RecyclerView {
// also be used in DotIndicatorDecoration.
float mEdgeToCenterDistance = Float.MAX_VALUE;
private float mCardCenterToScreenCenterDistancePx = Float.MAX_VALUE;
- // When card data is loaded, this many cards should be animated as data is bound to them.
- private int mNumCardsToAnimate;
- // When card data is loaded, this is the position of the leftmost card to be animated.
- private int mCardAnimationStartPosition;
- // When card data is loaded, the animations may be delayed so that other animations can complete
- private int mExtraAnimationDelay;
interface OnSelectionListener {
/**
@@ -179,10 +173,6 @@ public class WalletCardCarousel extends RecyclerView {
}
}
- void setExtraAnimationDelay(int extraAnimationDelay) {
- mExtraAnimationDelay = extraAnimationDelay;
- }
-
void setSelectionListener(OnSelectionListener selectionListener) {
mSelectionListener = selectionListener;
}
@@ -200,19 +190,14 @@ public class WalletCardCarousel extends RecyclerView {
}
/**
- * Set card data. Returns true if carousel was empty, indicating that views will be animated
+ * Returns true if the data set is changed.
*/
- boolean setData(List<WalletCardViewInfo> data, int selectedIndex) {
- boolean wasEmpty = mWalletCardCarouselAdapter.getItemCount() == 0;
- mWalletCardCarouselAdapter.setData(data);
+ boolean setData(List<WalletCardViewInfo> data, int selectedIndex, boolean hasLockStateChanged) {
+ boolean hasDataChanged = mWalletCardCarouselAdapter.setData(data, hasLockStateChanged);
scrollToPosition(selectedIndex);
- if (wasEmpty) {
- mNumCardsToAnimate = numCardsOnScreen(data.size(), selectedIndex);
- mCardAnimationStartPosition = Math.max(selectedIndex - 1, 0);
- }
WalletCardViewInfo selectedCard = data.get(selectedIndex);
mCardScrollListener.onCardScroll(selectedCard, selectedCard, 0);
- return wasEmpty;
+ return hasDataChanged;
}
@Override
@@ -222,19 +207,6 @@ public class WalletCardCarousel extends RecyclerView {
}
/**
- * The number of cards shown on screen when one of the cards is position in the center. This is
- * also the num
- */
- private static int numCardsOnScreen(int numCards, int selectedIndex) {
- if (numCards <= 2) {
- return numCards;
- }
- // When there are 3 or more cards, 3 cards will be shown unless the first or last card is
- // centered on screen.
- return selectedIndex > 0 && selectedIndex < (numCards - 1) ? 3 : 2;
- }
-
- /**
* The padding pushes the first and last cards in the list to the center when they are
* selected.
*/
@@ -439,9 +411,29 @@ public class WalletCardCarousel extends RecyclerView {
return mData.get(position).getCardId().hashCode();
}
- void setData(List<WalletCardViewInfo> data) {
+ private boolean setData(List<WalletCardViewInfo> data, boolean hasLockedStateChanged) {
+ List<WalletCardViewInfo> oldData = mData;
mData = data;
- notifyDataSetChanged();
+ if (hasLockedStateChanged || !isUiEquivalent(oldData, data)) {
+ notifyDataSetChanged();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isUiEquivalent(
+ List<WalletCardViewInfo> oldData, List<WalletCardViewInfo> newData) {
+ if (oldData.size() != newData.size()) {
+ return false;
+ }
+ for (int i = 0; i < newData.size(); i++) {
+ WalletCardViewInfo oldItem = oldData.get(i);
+ WalletCardViewInfo newItem = newData.get(i);
+ if (!oldItem.isUiEquivalent(newItem)) {
+ return false;
+ }
+ }
+ return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewInfo.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewInfo.java
index 3d37320f3d32..08fbe4aa3ce5 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewInfo.java
@@ -54,4 +54,11 @@ interface WalletCardViewInfo {
*/
@NonNull
PendingIntent getPendingIntent();
+
+ default boolean isUiEquivalent(WalletCardViewInfo other) {
+ if (other == null) {
+ return false;
+ }
+ return getCardId().equals(other.getCardId());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index 44074f706183..c547bb346617 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -19,8 +19,6 @@ package com.android.systemui.wallet.ui;
import static com.android.systemui.wallet.ui.WalletCardCarousel.CARD_ANIM_ALPHA_DELAY;
import static com.android.systemui.wallet.ui.WalletCardCarousel.CARD_ANIM_ALPHA_DURATION;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
@@ -29,6 +27,7 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
+import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -46,9 +45,8 @@ import java.util.List;
public class WalletView extends FrameLayout implements WalletCardCarousel.OnCardScrollListener {
private static final String TAG = "WalletView";
- private static final int CAROUSEL_IN_ANIMATION_DURATION = 300;
+ private static final int CAROUSEL_IN_ANIMATION_DURATION = 100;
private static final int CAROUSEL_OUT_ANIMATION_DURATION = 200;
- private static final int CARD_LABEL_ANIM_DELAY = 133;
private final WalletCardCarousel mCardCarousel;
private final ImageView mIcon;
@@ -57,14 +55,12 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
private final Button mAppButton;
// Displays underneath the carousel, allow user to unlock device, verify card, etc.
private final Button mActionButton;
- private final Interpolator mInInterpolator;
private final Interpolator mOutInterpolator;
private final float mAnimationTranslationX;
private final ViewGroup mCardCarouselContainer;
private final TextView mErrorView;
private final ViewGroup mEmptyStateView;
private CharSequence mCenterCardText;
- private Drawable mCenterCardIcon;
private boolean mIsDeviceLocked = false;
public WalletView(Context context) {
@@ -83,8 +79,6 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
mActionButton = requireViewById(R.id.wallet_action_button);
mErrorView = requireViewById(R.id.error_view);
mEmptyStateView = requireViewById(R.id.wallet_empty_state);
- mInInterpolator =
- AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
mOutInterpolator =
AnimationUtils.loadInterpolator(context, android.R.interpolator.accelerate_cubic);
mAnimationTranslationX = mCardCarousel.getCardWidthPx() / 4f;
@@ -109,7 +103,6 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
Drawable centerCardIcon = centerCard.getIcon();
if (!TextUtils.equals(mCenterCardText, centerCardText)) {
mCenterCardText = centerCardText;
- mCenterCardIcon = centerCardIcon;
mCardLabel.setText(centerCardText);
mIcon.setImageDrawable(centerCardIcon);
}
@@ -134,39 +127,15 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
*/
void showCardCarousel(
List<WalletCardViewInfo> data, int selectedIndex, boolean isDeviceLocked) {
+ boolean shouldAnimate =
+ mCardCarousel.setData(data, selectedIndex, mIsDeviceLocked != isDeviceLocked);
mIsDeviceLocked = isDeviceLocked;
- boolean shouldAnimate = mCardCarousel.setData(data, selectedIndex);
mCardCarouselContainer.setVisibility(VISIBLE);
mErrorView.setVisibility(GONE);
+ mEmptyStateView.setVisibility(GONE);
renderHeaderIconAndActionButton(data.get(selectedIndex), isDeviceLocked);
if (shouldAnimate) {
- // If the empty state is visible, animate it away and delay the card carousel animation
- int emptyStateAnimDelay = 0;
- if (mEmptyStateView.getVisibility() == VISIBLE) {
- emptyStateAnimDelay = CARD_ANIM_ALPHA_DURATION;
- mEmptyStateView.animate()
- .alpha(0)
- .setDuration(emptyStateAnimDelay)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mEmptyStateView.setVisibility(GONE);
- }
- })
- .start();
- }
- mCardLabel.setAlpha(0f);
- mCardLabel.animate().alpha(1f)
- .setStartDelay(CARD_LABEL_ANIM_DELAY + emptyStateAnimDelay)
- .setDuration(CARD_ANIM_ALPHA_DURATION)
- .start();
- mCardCarousel.setExtraAnimationDelay(emptyStateAnimDelay);
- mCardCarousel.setTranslationX(mAnimationTranslationX);
- mCardCarousel.animate().translationX(0)
- .setInterpolator(mInInterpolator)
- .setDuration(CAROUSEL_IN_ANIMATION_DURATION)
- .setStartDelay(emptyStateAnimDelay)
- .start();
+ animateViewsShown(mIcon, mCardLabel, mActionButton);
}
}
@@ -277,6 +246,15 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
}
}
+ private static void animateViewsShown(View... uiElements) {
+ for (View view : uiElements) {
+ if (view.getVisibility() == VISIBLE) {
+ view.setAlpha(0f);
+ view.animate().alpha(1f).setDuration(CAROUSEL_IN_ANIMATION_DURATION).start();
+ }
+ }
+ }
+
private static CharSequence getLabelText(WalletCardViewInfo card) {
String[] rawLabel = card.getLabel().toString().split("\\n");
return rawLabel.length == 2 ? rawLabel[0] : card.getLabel();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
index 54cee847b757..24e47c5159a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
@@ -35,7 +35,7 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
-import com.android.systemui.qs.tileimpl.QSTileView;
+import com.android.systemui.qs.tileimpl.QSTileViewImpl;
import org.junit.Before;
import org.junit.Test;
@@ -60,7 +60,7 @@ public class TileLayoutTest extends SysuiTestCase {
private QSPanelControllerBase.TileRecord createTileRecord() {
QSPanelControllerBase.TileRecord tileRecord = new QSPanelControllerBase.TileRecord();
tileRecord.tile = mock(QSTile.class);
- tileRecord.tileView = spy(new QSTileView(mContext, new QSIconViewImpl(mContext)));
+ tileRecord.tileView = spy(new QSTileViewImpl(mContext, new QSIconViewImpl(mContext)));
return tileRecord;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileBaseViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index 998e070009cc..e5e2e53b0a09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileBaseViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -16,9 +16,12 @@
package com.android.systemui.qs.tileimpl
+import android.content.Context
+import android.graphics.drawable.Drawable
import android.service.quicksettings.Tile
import android.testing.AndroidTestingRunner
import android.text.TextUtils
+import android.view.View
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
@@ -33,18 +36,24 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@SmallTest
-class QSTileBaseViewTest : SysuiTestCase() {
+class QSTileViewImplTest : SysuiTestCase() {
@Mock
private lateinit var iconView: QSIconView
+ @Mock
+ private lateinit var customDrawable: Drawable
- private lateinit var tileView: QSTileBaseView
+ private lateinit var tileView: FakeTileView
+ private lateinit var customDrawableView: View
+ private lateinit var chevronView: View
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- tileView = QSTileBaseView(context, iconView, false)
+ tileView = FakeTileView(context, iconView, false)
+ customDrawableView = tileView.requireViewById(R.id.customDrawable)
+ chevronView = tileView.requireViewById(R.id.chevron)
}
@Test
@@ -54,7 +63,7 @@ class QSTileBaseViewTest : SysuiTestCase() {
state.state = Tile.STATE_UNAVAILABLE
state.secondaryLabel = testString
- tileView.handleStateChanged(state)
+ tileView.changeState(state)
assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
}
@@ -66,7 +75,7 @@ class QSTileBaseViewTest : SysuiTestCase() {
state.state = Tile.STATE_INACTIVE
state.secondaryLabel = testString
- tileView.handleStateChanged(state)
+ tileView.changeState(state)
assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
}
@@ -78,7 +87,7 @@ class QSTileBaseViewTest : SysuiTestCase() {
state.state = Tile.STATE_ACTIVE
state.secondaryLabel = testString
- tileView.handleStateChanged(state)
+ tileView.changeState(state)
assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
}
@@ -89,7 +98,7 @@ class QSTileBaseViewTest : SysuiTestCase() {
state.state = Tile.STATE_INACTIVE
state.secondaryLabel = ""
- tileView.handleStateChanged(state)
+ tileView.changeState(state)
assertThat(TextUtils.isEmpty(state.secondaryLabel)).isTrue()
}
@@ -100,7 +109,7 @@ class QSTileBaseViewTest : SysuiTestCase() {
state.state = Tile.STATE_ACTIVE
state.secondaryLabel = ""
- tileView.handleStateChanged(state)
+ tileView.changeState(state)
assertThat(TextUtils.isEmpty(state.secondaryLabel)).isTrue()
}
@@ -111,7 +120,7 @@ class QSTileBaseViewTest : SysuiTestCase() {
state.state = Tile.STATE_UNAVAILABLE
state.secondaryLabel = ""
- tileView.handleStateChanged(state)
+ tileView.changeState(state)
assertThat(state.secondaryLabel as CharSequence).isEqualTo(
context.getString(R.string.tile_unavailable)
@@ -124,7 +133,7 @@ class QSTileBaseViewTest : SysuiTestCase() {
state.state = Tile.STATE_INACTIVE
state.secondaryLabel = ""
- tileView.handleStateChanged(state)
+ tileView.changeState(state)
assertThat(state.secondaryLabel as CharSequence).isEqualTo(
context.getString(R.string.switch_bar_off)
@@ -137,10 +146,85 @@ class QSTileBaseViewTest : SysuiTestCase() {
state.state = Tile.STATE_ACTIVE
state.secondaryLabel = ""
- tileView.handleStateChanged(state)
+ tileView.changeState(state)
assertThat(state.secondaryLabel as CharSequence).isEqualTo(
context.getString(R.string.switch_bar_on)
)
}
+
+ @Test
+ fun testShowCustomDrawableViewBooleanState() {
+ val state = QSTile.BooleanState()
+ state.sideViewCustomDrawable = customDrawable
+
+ tileView.changeState(state)
+
+ assertThat(customDrawableView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(chevronView.visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun testShowCustomDrawableViewNonBooleanState() {
+ val state = QSTile.State()
+ state.sideViewCustomDrawable = customDrawable
+
+ tileView.changeState(state)
+
+ assertThat(customDrawableView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(chevronView.visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun testShowCustomDrawableViewBooleanStateForceChevron() {
+ val state = QSTile.BooleanState()
+ state.sideViewCustomDrawable = customDrawable
+ state.forceExpandIcon = true
+
+ tileView.changeState(state)
+
+ assertThat(customDrawableView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(chevronView.visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun testShowChevronNonBooleanState() {
+ val state = QSTile.State()
+
+ tileView.changeState(state)
+
+ assertThat(customDrawableView.visibility).isEqualTo(View.GONE)
+ assertThat(chevronView.visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun testShowChevronBooleanStateForcheShow() {
+ val state = QSTile.BooleanState()
+ state.forceExpandIcon = true
+
+ tileView.changeState(state)
+
+ assertThat(customDrawableView.visibility).isEqualTo(View.GONE)
+ assertThat(chevronView.visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun testNoImageShown() {
+ val state = QSTile.BooleanState()
+
+ tileView.changeState(state)
+
+ assertThat(customDrawableView.visibility).isEqualTo(View.GONE)
+ assertThat(chevronView.visibility).isEqualTo(View.GONE)
+ }
+
+ class FakeTileView(
+ context: Context,
+ icon: QSIconView,
+ collapsed: Boolean
+ ) : QSTileViewImpl(context, icon, collapsed) {
+ fun changeState(state: QSTile.State) {
+ handleStateChanged(state)
+ }
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index 9d463d458479..e894b7bb4c77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -271,7 +271,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
mContext.getString(R.string.wallet_secondary_label_device_locked),
state.secondaryLabel);
assertNotNull(state.stateDescription);
- assertNull(state.sideViewDrawable);
+ assertNull(state.sideViewCustomDrawable);
}
@Test
@@ -287,7 +287,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
"•••• 1234",
state.secondaryLabel);
assertNotNull(state.stateDescription);
- assertNotNull(state.sideViewDrawable);
+ assertNotNull(state.sideViewCustomDrawable);
}
@@ -303,7 +303,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
mContext.getString(R.string.wallet_secondary_label_no_card),
state.secondaryLabel);
assertNotNull(state.stateDescription);
- assertNull(state.sideViewDrawable);
+ assertNull(state.sideViewCustomDrawable);
}
@Test
@@ -315,7 +315,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
assertEquals(Tile.STATE_UNAVAILABLE, state.state);
assertNull(state.stateDescription);
- assertNull(state.sideViewDrawable);
+ assertNull(state.sideViewCustomDrawable);
}
@Test
@@ -342,7 +342,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
when(mKeyguardStateController.isUnlocked()).thenReturn(true);
setUpWalletCard(/* hasCard= */ true);
- assertNotNull(mTile.getState().sideViewDrawable);
+ assertNotNull(mTile.getState().sideViewCustomDrawable);
}
@Test
@@ -362,7 +362,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
mCallbackCaptor.getValue().onWalletCardsRetrieved(responseWithCards);
mTestableLooper.processAllMessages();
- assertNotNull(mTile.getState().sideViewDrawable);
+ assertNotNull(mTile.getState().sideViewCustomDrawable);
mTile.handleSetListening(true);
@@ -373,14 +373,14 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
mCallbackCaptor.getValue().onWalletCardsRetrieved(responseWithoutCards);
mTestableLooper.processAllMessages();
- assertNull(mTile.getState().sideViewDrawable);
+ assertNull(mTile.getState().sideViewCustomDrawable);
}
@Test
public void testQueryCards_noCards_notUpdateSideViewDrawable() {
setUpWalletCard(/* hasCard= */ false);
- assertNull(mTile.getState().sideViewDrawable);
+ assertNull(mTile.getState().sideViewCustomDrawable);
}
@Test
@@ -395,7 +395,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
mCallbackCaptor.getValue().onWalletCardRetrievalError(error);
mTestableLooper.processAllMessages();
- assertNull(mTile.getState().sideViewDrawable);
+ assertNull(mTile.getState().sideViewCustomDrawable);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
new file mode 100644
index 000000000000..0e77bb36d68f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2021 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.ongoingcall
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.LinearLayout
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+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
+
+private const val TEXT_VIEW_MAX_WIDTH = 400
+
+// When a [Chronometer] is created, it starts off with "00:00" as its text.
+private const val INITIAL_TEXT = "00:00"
+private const val LARGE_TEXT = "00:000"
+private const val XL_TEXT = "00:0000"
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class OngoingCallChronometerTest : SysuiTestCase() {
+
+ private lateinit var textView: OngoingCallChronometer
+ private lateinit var doesNotFitText: String
+
+ @Before
+ fun setUp() {
+ allowTestableLooperAsMainThread()
+ TestableLooper.get(this).runWithLooper {
+ val chipView = LayoutInflater.from(mContext)
+ .inflate(R.layout.ongoing_call_chip, null) as LinearLayout
+ textView = chipView.findViewById(R.id.ongoing_call_chip_time)!!
+ measureTextView()
+ calculateDoesNotFixText()
+ }
+ }
+
+ @Test
+ fun verifyTextSizes() {
+ val initialTextLength = textView.paint.measureText(INITIAL_TEXT)
+ val largeTextLength = textView.paint.measureText(LARGE_TEXT)
+ val xlTextLength = textView.paint.measureText(XL_TEXT)
+
+ // Assert that our test text sizes do what we expect them to do in the rest of the tests.
+ assertThat(initialTextLength).isLessThan(TEXT_VIEW_MAX_WIDTH)
+ assertThat(largeTextLength).isLessThan(TEXT_VIEW_MAX_WIDTH)
+ assertThat(xlTextLength).isLessThan(TEXT_VIEW_MAX_WIDTH)
+ assertThat(textView.paint.measureText(doesNotFitText)).isGreaterThan(TEXT_VIEW_MAX_WIDTH)
+
+ assertThat(largeTextLength).isGreaterThan(initialTextLength)
+ assertThat(xlTextLength).isGreaterThan(largeTextLength)
+ }
+
+ @Test
+ fun onMeasure_initialTextFitsInSpace_textDisplayed() {
+ assertThat(textView.measuredWidth).isGreaterThan(0)
+ }
+
+ @Test
+ fun onMeasure_newTextLargerThanPreviousText_widthGetsLarger() {
+ val initialTextLength = textView.measuredWidth
+
+ setTextAndMeasure(LARGE_TEXT)
+
+ assertThat(textView.measuredWidth).isGreaterThan(initialTextLength)
+ }
+
+ @Test
+ fun onMeasure_newTextSmallerThanPreviousText_widthDoesNotGetSmaller() {
+ setTextAndMeasure(XL_TEXT)
+ val xlWidth = textView.measuredWidth
+
+ setTextAndMeasure(LARGE_TEXT)
+
+ assertThat(textView.measuredWidth).isEqualTo(xlWidth)
+ }
+
+ @Test
+ fun onMeasure_textDoesNotFit_textHidden() {
+ setTextAndMeasure(doesNotFitText)
+
+ assertThat(textView.measuredWidth).isEqualTo(0)
+ }
+
+ @Test
+ fun onMeasure_newTextFitsButPreviousTextDidNot_textHidden() {
+ setTextAndMeasure(doesNotFitText)
+
+ setTextAndMeasure(LARGE_TEXT)
+
+ assertThat(textView.measuredWidth).isEqualTo(0)
+ }
+
+ @Test
+ fun resetBase_hadLongerTextThenSetBaseThenShorterText_widthIsShort() {
+ setTextAndMeasure(XL_TEXT)
+ val xlWidth = textView.measuredWidth
+
+ textView.base = 0L
+ setTextAndMeasure(INITIAL_TEXT)
+
+ assertThat(textView.measuredWidth).isLessThan(xlWidth)
+ assertThat(textView.measuredWidth).isGreaterThan(0)
+ }
+
+ @Test
+ fun setBase_wasHidingTextThenSetBaseThenShorterText_textShown() {
+ setTextAndMeasure(doesNotFitText)
+
+ textView.base = 0L
+ setTextAndMeasure(INITIAL_TEXT)
+
+ assertThat(textView.measuredWidth).isGreaterThan(0)
+ }
+
+ private fun setTextAndMeasure(text: String) {
+ textView.text = text
+ measureTextView()
+ }
+
+ private fun measureTextView() {
+ textView.measure(
+ View.MeasureSpec.makeMeasureSpec(TEXT_VIEW_MAX_WIDTH, View.MeasureSpec.AT_MOST),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ )
+ }
+
+ /**
+ * Calculates what [doesNotFitText] should be. Needs to be done dynamically because different
+ * devices have different densities, which means the textView can fit different amounts of
+ * characters.
+ */
+ private fun calculateDoesNotFixText() {
+ var currentText = XL_TEXT + "0"
+ while (textView.paint.measureText(currentText) <= TEXT_VIEW_MAX_WIDTH) {
+ currentText += "0"
+ }
+ doesNotFitText = currentText
+ }
+}
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index c23f1cab0614..ed25452cf402 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -38,7 +38,7 @@ import java.util.concurrent.TimeUnit;
/**
* Thread pool used during initialization of system server.
*
- * <p>System services can {@link #submit(Runnable)} tasks for execution during boot.
+ * <p>System services can {@link #submit(Runnable, String)} tasks for execution during boot.
* The pool will be shut down after {@link SystemService#PHASE_BOOT_COMPLETED}.
*
* <p>New tasks <em>should not</em> be submitted afterwards.
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index 6c81de6af402..39321bfe6435 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -70,12 +70,20 @@ public abstract class SystemService {
/** @hide */
protected static final boolean DEBUG_USER = false;
- /*
+ /**
* The earliest boot phase the system send to system services on boot.
*/
public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100;
/**
+ * Boot phase that blocks on SensorService availability. The service gets started
+ * asynchronously since it may take awhile to actually finish initializing.
+ *
+ * @hide
+ */
+ public static final int PHASE_WAIT_FOR_SENSOR_SERVICE = 200;
+
+ /**
* After receiving this boot phase, services can obtain lock settings data.
*/
public static final int PHASE_LOCK_SETTINGS_READY = 480;
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 57254c1b9225..df4c2cfc7fd4 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -87,6 +87,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
@@ -429,6 +430,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {
public void onNewSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
// Startup VCN instances
synchronized (mLock) {
+ final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot;
mLastSnapshot = snapshot;
// Start any VCN instances as necessary
@@ -476,11 +478,29 @@ public class VcnManagementService extends IVcnManagementService.Stub {
entry.getValue().updateSubscriptionSnapshot(mLastSnapshot);
}
}
+
+ final Map<ParcelUuid, Set<Integer>> oldSubGrpMappings =
+ getSubGroupToSubIdMappings(oldSnapshot);
+ final Map<ParcelUuid, Set<Integer>> currSubGrpMappings =
+ getSubGroupToSubIdMappings(mLastSnapshot);
+ if (!currSubGrpMappings.equals(oldSubGrpMappings)) {
+ notifyAllPolicyListenersLocked();
+ }
}
}
}
@GuardedBy("mLock")
+ private Map<ParcelUuid, Set<Integer>> getSubGroupToSubIdMappings(
+ @NonNull TelephonySubscriptionSnapshot snapshot) {
+ final Map<ParcelUuid, Set<Integer>> subGrpMappings = new ArrayMap<>();
+ for (ParcelUuid subGrp : mVcns.keySet()) {
+ subGrpMappings.put(subGrp, snapshot.getAllSubIdsInGroup(subGrp));
+ }
+ return subGrpMappings;
+ }
+
+ @GuardedBy("mLock")
private void stopVcnLocked(@NonNull ParcelUuid uuidToTeardown) {
final Vcn vcnToTeardown = mVcns.remove(uuidToTeardown);
if (vcnToTeardown == null) {
@@ -813,6 +833,8 @@ public class VcnManagementService extends IVcnManagementService.Stub {
if (isVcnManagedNetwork) {
ncBuilder.removeCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ } else {
+ ncBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
}
if (isRestrictedCarrierWifi) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d526ebc8e581..6a3cb6f22886 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7777,16 +7777,25 @@ public class ActivityManagerService extends IActivityManager.Stub
incrementalMetrics != null /* isIncremental */, loadingProgress,
incrementalMetrics != null ? incrementalMetrics.getMillisSinceOldestPendingRead()
: -1,
- 0 /* storage_health_code */,
- 0 /* data_loader_status_code */,
- false /* read_logs_enabled */,
- 0 /* millis_since_last_data_loader_bind */,
- 0 /* data_loader_bind_delay_millis */,
- 0 /* total_delayed_reads */,
- 0 /* total_failed_reads */,
- 0 /* last_read_error_uid */,
- 0 /* last_read_error_millis_since */,
- 0 /* last_read_error_code */
+ incrementalMetrics != null ? incrementalMetrics.getStorageHealthStatusCode()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getDataLoaderStatusCode()
+ : -1,
+ incrementalMetrics != null && incrementalMetrics.getReadLogsEnabled(),
+ incrementalMetrics != null ? incrementalMetrics.getMillisSinceLastDataLoaderBind()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getDataLoaderBindDelayMillis()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getTotalDelayedReads()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getTotalFailedReads()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getLastReadErrorUid()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getMillisSinceLastReadError()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getLastReadErrorNumber()
+ : 0
);
final int relaunchReason = r == null ? RELAUNCH_REASON_NONE
@@ -15831,8 +15840,12 @@ public class ActivityManagerService extends IActivityManager.Stub
if (initLocale || !mProcessesReady) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}
+ final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
+ bOptions.setTemporaryAppAllowlist(mInternal.getBootTimeTempAllowListDuration(),
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ PowerExemptionManager.REASON_LOCALE_CHANGED, "");
broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
- null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ null, OP_NONE, bOptions.toBundle(), false, false, MY_PID, SYSTEM_UID,
Binder.getCallingUid(), Binder.getCallingPid(),
UserHandle.USER_ALL);
}
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index c1b2a9c1987a..5a7e14a6907a 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -433,16 +433,25 @@ class ProcessErrorStateRecord {
incrementalMetrics != null /* isIncremental */, loadingProgress,
incrementalMetrics != null ? incrementalMetrics.getMillisSinceOldestPendingRead()
: -1,
- 0 /* storage_health_code */,
- 0 /* data_loader_status_code */,
- false /* read_logs_enabled */,
- 0 /* millis_since_last_data_loader_bind */,
- 0 /* data_loader_bind_delay_millis */,
- 0 /* total_delayed_reads */,
- 0 /* total_failed_reads */,
- 0 /* last_read_error_uid */,
- 0 /* last_read_error_millis_since */,
- 0 /* last_read_error_code */);
+ incrementalMetrics != null ? incrementalMetrics.getStorageHealthStatusCode()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getDataLoaderStatusCode()
+ : -1,
+ incrementalMetrics != null && incrementalMetrics.getReadLogsEnabled(),
+ incrementalMetrics != null ? incrementalMetrics.getMillisSinceLastDataLoaderBind()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getDataLoaderBindDelayMillis()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getTotalDelayedReads()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getTotalFailedReads()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getLastReadErrorUid()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getMillisSinceLastReadError()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getLastReadErrorNumber()
+ : 0);
final ProcessRecord parentPr = parentProcess != null
? (ProcessRecord) parentProcess.mOwner : null;
mService.addErrorToDropBox("anr", mApp, mApp.processName, activityShortComponentName,
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index cc98abfc17f2..af3f658ad800 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3418,6 +3418,11 @@ public final class ProcessList {
return;
}
+ if (app.getPid() == 0 && !app.isPendingStart()) {
+ // This process has been killed and its cleanup is done, don't proceed the LRU update.
+ return;
+ }
+
synchronized (mProcLock) {
updateLruProcessLSP(app, client, hasActivity, hasService);
}
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 9396241c3f98..78ff67a63dcb 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -182,12 +182,18 @@ public final class AppHibernationService extends SystemService {
NAMESPACE_APP_HIBERNATION,
ActivityThread.currentApplication().getMainExecutor(),
this::onDeviceConfigChanged);
- getContext().getSystemService(StatsManager.class)
- .setPullAtomCallback(
- FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS,
- /* metadata */ null, // use default PullAtomMetadata values
- mBackgroundExecutor,
- new StatsPullAtomCallbackImpl());
+ final StatsManager statsManager = getContext().getSystemService(StatsManager.class);
+ final StatsPullAtomCallbackImpl pullAtomCallback = new StatsPullAtomCallbackImpl();
+ statsManager.setPullAtomCallback(
+ FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS,
+ /* metadata */ null, // use default PullAtomMetadata values
+ mBackgroundExecutor,
+ pullAtomCallback);
+ statsManager.setPullAtomCallback(
+ FrameworkStatsLog.GLOBAL_HIBERNATED_APPS,
+ /* metadata */ null, // use default PullAtomMetadata values
+ mBackgroundExecutor,
+ pullAtomCallback);
}
}
@@ -291,6 +297,7 @@ public final class AppHibernationService extends SystemService {
stateSnapshot.packageName,
userIdSnapshot,
stateSnapshot.hibernated,
+ // TODO(b/187224817): This isn't the expected value right now.
stateSnapshot.lastUnhibernatedMs);
});
List<UserLevelState> states = new ArrayList<>(mUserStates.get(userId).values());
@@ -938,23 +945,39 @@ public final class AppHibernationService extends SystemService {
private final class StatsPullAtomCallbackImpl implements StatsPullAtomCallback {
@Override
public int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) {
- if (atomTag != FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS) {
- return StatsManager.PULL_SKIP;
+ if (!isAppHibernationEnabled()
+ && (atomTag == FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS
+ || atomTag == FrameworkStatsLog.GLOBAL_HIBERNATED_APPS)) {
+ return StatsManager.PULL_SUCCESS;
}
- if (isAppHibernationEnabled()) {
- List<UserInfo> userInfos = mUserManager.getAliveUsers();
- final int numUsers = userInfos.size();
- for (int i = 0; i < numUsers; ++i) {
- final int userId = userInfos.get(i).id;
- if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
- data.add(
- FrameworkStatsLog.buildStatsEvent(
- atomTag,
- getHibernatingPackagesForUser(userId).size(),
- userId)
- );
+
+ switch (atomTag) {
+ case FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS:
+ List<UserInfo> userInfos = mUserManager.getAliveUsers();
+ final int numUsers = userInfos.size();
+ for (int i = 0; i < numUsers; ++i) {
+ final int userId = userInfos.get(i).id;
+ if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
+ data.add(
+ FrameworkStatsLog.buildStatsEvent(
+ atomTag,
+ getHibernatingPackagesForUser(userId).size(),
+ userId)
+ );
+ }
}
- }
+ break;
+ case FrameworkStatsLog.GLOBAL_HIBERNATED_APPS:
+ int hibernatedAppCount = 0;
+ synchronized (mLock) {
+ for (GlobalLevelState state : mGlobalHibernationStates.values()) {
+ if (state.hibernated) hibernatedAppCount++;
+ }
+ }
+ data.add(FrameworkStatsLog.buildStatsEvent(atomTag, hibernatedAppCount));
+ break;
+ default:
+ return StatsManager.PULL_SKIP;
}
return StatsManager.PULL_SUCCESS;
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7eaf18fc971b..99b0d81285f0 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3065,6 +3065,13 @@ public class AppOpsService extends IAppOpsService.Stub {
shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation);
}
+ // TODO b/184963112: remove once full blaming is implemented
+ private boolean isRecognitionServiceTemp(int code, String packageName) {
+ return code == OP_RECORD_AUDIO
+ && (packageName.equals("com.google.android.googlequicksearchbox")
+ || packageName.equals("com.google.android.tts"));
+ }
+
private SyncNotedAppOp noteProxyOperationImpl(int code, AttributionSource attributionSource,
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
boolean skipProxyOperation) {
@@ -3085,13 +3092,15 @@ public class AppOpsService extends IAppOpsService.Stub {
String resolveProxyPackageName = AppOpsManager.resolvePackageName(proxyUid,
proxyPackageName);
if (resolveProxyPackageName == null) {
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code,
+ proxiedAttributionTag, proxiedPackageName);
}
final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
final boolean isProxyTrusted = mContext.checkPermission(
Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
- == PackageManager.PERMISSION_GRANTED || isSelfBlame;
+ == PackageManager.PERMISSION_GRANTED || isSelfBlame
+ || isRecognitionServiceTemp(code, proxyPackageName);
if (!skipProxyOperation) {
final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
@@ -3101,14 +3110,16 @@ public class AppOpsService extends IAppOpsService.Stub {
resolveProxyPackageName, proxyAttributionTag, Process.INVALID_UID, null, null,
proxyFlags, !isProxyTrusted, "proxy " + message, shouldCollectMessage);
if (proxyReturn.getOpMode() != AppOpsManager.MODE_ALLOWED) {
- return new SyncNotedAppOp(proxyReturn.getOpMode(), code, proxiedAttributionTag);
+ return new SyncNotedAppOp(proxyReturn.getOpMode(), code, proxiedAttributionTag,
+ proxiedPackageName);
}
}
String resolveProxiedPackageName = AppOpsManager.resolvePackageName(proxiedUid,
proxiedPackageName);
if (resolveProxiedPackageName == null) {
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag,
+ proxiedPackageName);
}
final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED
@@ -3135,7 +3146,8 @@ public class AppOpsService extends IAppOpsService.Stub {
String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
+ packageName);
}
return noteOperationUnchecked(code, uid, resolvedPackageName, attributionTag,
Process.INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF,
@@ -3152,7 +3164,8 @@ public class AppOpsService extends IAppOpsService.Stub {
bypass = verifyAndGetBypass(uid, packageName, attributionTag);
} catch (SecurityException e) {
Slog.e(TAG, "noteOperation", e);
- return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
+ packageName);
}
synchronized (this) {
@@ -3163,7 +3176,8 @@ public class AppOpsService extends IAppOpsService.Stub {
AppOpsManager.MODE_IGNORED);
if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
+ " package " + packageName);
- return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
+ packageName);
}
final Op op = getOpLocked(ops, code, uid, true);
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
@@ -3179,7 +3193,8 @@ public class AppOpsService extends IAppOpsService.Stub {
attributedOp.rejected(uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
AppOpsManager.MODE_IGNORED);
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
+ packageName);
}
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
@@ -3192,7 +3207,7 @@ public class AppOpsService extends IAppOpsService.Stub {
attributedOp.rejected(uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
uidMode);
- return new SyncNotedAppOp(uidMode, code, attributionTag);
+ return new SyncNotedAppOp(uidMode, code, attributionTag, packageName);
}
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
@@ -3205,7 +3220,7 @@ public class AppOpsService extends IAppOpsService.Stub {
attributedOp.rejected(uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
mode);
- return new SyncNotedAppOp(mode, code, attributionTag);
+ return new SyncNotedAppOp(mode, code, attributionTag, packageName);
}
}
if (DEBUG) {
@@ -3224,7 +3239,8 @@ public class AppOpsService extends IAppOpsService.Stub {
shouldCollectMessage);
}
- return new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED, code, attributionTag,
+ packageName);
}
}
@@ -3528,7 +3544,8 @@ public class AppOpsService extends IAppOpsService.Stub {
String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
+ packageName);
}
// As a special case for OP_RECORD_AUDIO_HOTWORD, which we use only for attribution
@@ -3539,7 +3556,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (code == OP_RECORD_AUDIO_HOTWORD) {
int result = checkOperation(OP_RECORD_AUDIO, uid, packageName);
if (result != AppOpsManager.MODE_ALLOWED) {
- return new SyncNotedAppOp(result, code, attributionTag);
+ return new SyncNotedAppOp(result, code, attributionTag, packageName);
}
}
return startOperationUnchecked(clientId, code, uid, packageName, attributionTag,
@@ -3578,7 +3595,8 @@ public class AppOpsService extends IAppOpsService.Stub {
String resolvedProxyPackageName = AppOpsManager.resolvePackageName(proxyUid,
proxyPackageName);
if (resolvedProxyPackageName == null) {
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag,
+ proxiedPackageName);
}
final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
@@ -3589,7 +3607,8 @@ public class AppOpsService extends IAppOpsService.Stub {
String resolvedProxiedPackageName = AppOpsManager.resolvePackageName(proxiedUid,
proxiedPackageName);
if (resolvedProxiedPackageName == null) {
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag,
+ proxiedPackageName);
}
final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED
@@ -3637,7 +3656,8 @@ public class AppOpsService extends IAppOpsService.Stub {
bypass = verifyAndGetBypass(uid, packageName, attributionTag);
} catch (SecurityException e) {
Slog.e(TAG, "startOperation", e);
- return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
+ packageName);
}
synchronized (this) {
@@ -3649,7 +3669,8 @@ public class AppOpsService extends IAppOpsService.Stub {
}
if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
+ " package " + packageName);
- return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
+ packageName);
}
final Op op = getOpLocked(ops, code, uid, true);
if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
@@ -3657,7 +3678,8 @@ public class AppOpsService extends IAppOpsService.Stub {
scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
flags, AppOpsManager.MODE_IGNORED);
}
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
+ packageName);
}
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
@@ -3678,7 +3700,7 @@ public class AppOpsService extends IAppOpsService.Stub {
scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
flags, uidMode);
}
- return new SyncNotedAppOp(uidMode, code, attributionTag);
+ return new SyncNotedAppOp(uidMode, code, attributionTag, packageName);
}
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
@@ -3694,7 +3716,7 @@ public class AppOpsService extends IAppOpsService.Stub {
scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
flags, mode);
}
- return new SyncNotedAppOp(mode, code, attributionTag);
+ return new SyncNotedAppOp(mode, code, attributionTag, packageName);
}
}
if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
@@ -3716,7 +3738,8 @@ public class AppOpsService extends IAppOpsService.Stub {
message, shouldCollectMessage);
}
- return new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED, code, attributionTag,
+ packageName);
}
@Override
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 741946e147cd..1fcad62e3a07 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -31,8 +31,8 @@ public abstract class GenerateChallengeClient<T> extends HalClientMonitor<T> {
public GenerateChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
- @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, listener, 0 /* userId */, owner, 0 /* cookie */, sensorId,
+ int userId, @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN);
}
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 7f86c628028a..1edf5afef3b2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
@@ -26,8 +26,8 @@ import com.android.server.biometrics.BiometricsProto;
public abstract class RevokeChallengeClient<T> extends HalClientMonitor<T> {
public RevokeChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
- @NonNull IBinder token, @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, null /* listener */, 0 /* userId */, owner,
+ @NonNull IBinder token, int userId, @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, null /* listener */, userId, owner,
0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
index 5804622a545f..904c39922a06 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
@@ -35,8 +35,9 @@ public class FaceGenerateChallengeClient extends GenerateChallengeClient<ISessio
FaceGenerateChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, listener, owner, sensorId);
+ @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
+ int sensorId) {
+ super(context, lazyDaemon, token, listener, userId, owner, sensorId);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 84d239ef2b50..dd3057e721be 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -300,7 +300,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mHandler.post(() -> {
final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
- new ClientMonitorCallbackConverter(receiver), opPackageName, sensorId);
+ new ClientMonitorCallbackConverter(receiver), userId, opPackageName, sensorId);
scheduleForSensor(sensorId, client);
});
}
@@ -310,7 +310,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@NonNull String opPackageName, long challenge) {
mHandler.post(() -> {
final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
- mSensors.get(sensorId).getLazySession(), token, opPackageName, sensorId,
+ mSensors.get(sensorId).getLazySession(), token, userId, opPackageName, sensorId,
challenge);
scheduleForSensor(sensorId, client);
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
index 99bf893d5ce8..7a69c443411f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
@@ -37,8 +37,8 @@ public class FaceRevokeChallengeClient extends RevokeChallengeClient<ISession> {
FaceRevokeChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
- @NonNull String owner, int sensorId, long challenge) {
- super(context, lazyDaemon, token, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId, long challenge) {
+ super(context, lazyDaemon, token, userId, owner, sensorId);
mChallenge = challenge;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 5dfc5907060f..f908fba8693c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -518,8 +518,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
scheduleUpdateActiveUserWithoutHandler(userId);
final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName,
- mSensorId, mCurrentChallengeOwner);
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
+ opPackageName, mSensorId, mCurrentChallengeOwner);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -548,7 +548,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
}
final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
- mLazyDaemon, token, opPackageName, mSensorId);
+ mLazyDaemon, token, userId, opPackageName, mSensorId);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
index 24af817a262f..3e0064e496c7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
@@ -43,9 +43,9 @@ public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiomet
FaceGenerateChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId,
- @Nullable FaceGenerateChallengeClient interruptedClient) {
- super(context, lazyDaemon, token, listener, owner, sensorId);
+ @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
+ int sensorId, @Nullable FaceGenerateChallengeClient interruptedClient) {
+ super(context, lazyDaemon, token, listener, userId, owner, sensorId);
mInterruptedClient = interruptedClient;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
index ff3e770cdcb2..5ec7a9819b27 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
@@ -35,8 +35,8 @@ public class FaceRevokeChallengeClient extends RevokeChallengeClient<IBiometrics
FaceRevokeChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
- @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, userId, owner, sensorId);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
index 15a85e6dc309..293b57d0e890 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
@@ -37,8 +37,8 @@ class FingerprintGenerateChallengeClient extends GenerateChallengeClient<ISessio
@NonNull LazyDaemon<ISession> lazyDaemon,
@NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener,
- @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, listener, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, listener, userId, owner, sensorId);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 083df1929326..388bd0a1dc40 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -300,7 +300,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final FingerprintGenerateChallengeClient client =
new FingerprintGenerateChallengeClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
- new ClientMonitorCallbackConverter(receiver), opPackageName,
+ new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
sensorId);
scheduleForSensor(sensorId, client);
});
@@ -313,7 +313,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final FingerprintRevokeChallengeClient client =
new FingerprintRevokeChallengeClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
- opPackageName, sensorId, challenge);
+ userId, opPackageName, sensorId, challenge);
scheduleForSensor(sensorId, client);
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
index 90c69789b541..9e6f1bc2ff67 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
@@ -37,8 +37,8 @@ class FingerprintRevokeChallengeClient extends RevokeChallengeClient<ISession> {
FingerprintRevokeChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
- @NonNull String owner, int sensorId, long challenge) {
- super(context, lazyDaemon, token, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId, long challenge) {
+ super(context, lazyDaemon, token, userId, owner, sensorId);
mChallenge = challenge;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 3528690e6459..f0ea90dcbc33 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -528,7 +528,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mHandler.post(() -> {
final FingerprintGenerateChallengeClient client =
new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token,
- new ClientMonitorCallbackConverter(receiver), opPackageName,
+ new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
mSensorProperties.sensorId);
mScheduler.scheduleClientMonitor(client);
});
@@ -539,7 +539,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
@NonNull String opPackageName, long challenge) {
mHandler.post(() -> {
final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
- mContext, mLazyDaemon, token, opPackageName, mSensorProperties.sensorId);
+ mContext, mLazyDaemon, token, userId, opPackageName,
+ mSensorProperties.sensorId);
mScheduler.scheduleClientMonitor(client);
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
index 302ec2bd4c81..3584397eea81 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
@@ -38,8 +38,9 @@ public class FingerprintGenerateChallengeClient
FingerprintGenerateChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, listener, owner, sensorId);
+ @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
+ int sensorId) {
+ super(context, lazyDaemon, token, listener, userId, owner, sensorId);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
index 93d8ff3db177..b6b29b32d909 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
@@ -37,8 +37,8 @@ public class FingerprintRevokeChallengeClient
FingerprintRevokeChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
- @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, userId, owner, sensorId);
}
@Override
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 17f39ddac4fd..c3c42bae55b0 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -261,10 +261,12 @@ public final class ContentService extends IContentService.Stub {
pw.print(pidCounts.get(pid)); pw.println(" observers");
}
pw.println();
- pw.print(" Total number of nodes: "); pw.println(counts[0]);
- pw.print(" Total number of observers: "); pw.println(counts[1]);
+ pw.increaseIndent();
+ pw.print("Total number of nodes: "); pw.println(counts[0]);
+ pw.print("Total number of observers: "); pw.println(counts[1]);
- sObserverDeathDispatcher.dump(pw, " ");
+ sObserverDeathDispatcher.dump(pw);
+ pw.decreaseIndent();
}
synchronized (sObserverLeakDetectedUid) {
pw.println();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6083bc561219..44169a535e1c 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -645,7 +645,7 @@ public class NotificationManagerService extends SystemService {
sb.append("Archive (");
sb.append(N);
sb.append(" notification");
- sb.append((N==1)?")":"s)");
+ sb.append((N == 1) ? ")" : "s)");
return sb.toString();
}
@@ -718,6 +718,22 @@ public class NotificationManagerService extends SystemService {
}
}
}
+
+ void dumpImpl(PrintWriter pw, @NonNull DumpFilter filter) {
+ synchronized (mBufferLock) {
+ Iterator<Pair<StatusBarNotification, Integer>> iter = descendingIterator();
+ int i = 0;
+ while (iter.hasNext()) {
+ final StatusBarNotification sbn = iter.next().first;
+ if (filter != null && !filter.matches(sbn)) continue;
+ pw.println(" " + sbn);
+ if (++i >= 5) {
+ if (iter.hasNext()) pw.println(" ...");
+ break;
+ }
+ }
+ }
+ }
}
void loadDefaultApprovedServices(int userId) {
@@ -5928,17 +5944,7 @@ public class NotificationManagerService extends SystemService {
+ mPreferencesHelper.shouldHideSilentStatusIcons());
}
pw.println(" mArchive=" + mArchive.toString());
- Iterator<Pair<StatusBarNotification, Integer>> iter = mArchive.descendingIterator();
- int j=0;
- while (iter.hasNext()) {
- final StatusBarNotification sbn = iter.next().first;
- if (filter != null && !filter.matches(sbn)) continue;
- pw.println(" " + sbn);
- if (++j >= 5) {
- if (iter.hasNext()) pw.println(" ...");
- break;
- }
- }
+ mArchive.dumpImpl(pw, filter);
if (!zenOnly) {
N = mEnqueuedNotifications.size();
@@ -8304,6 +8310,21 @@ public class NotificationManagerService extends SystemService {
int rank, int count, boolean wasPosted, String listenerName) {
final String canceledKey = r.getKey();
+ // Get pending intent used to create alarm, use FLAG_NO_CREATE if PendingIntent
+ // does not already exist, then null will be returned.
+ final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
+ REQUEST_CODE_TIMEOUT,
+ new Intent(ACTION_NOTIFICATION_TIMEOUT)
+ .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
+ .appendPath(r.getKey()).build())
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
+ PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE);
+
+ // Cancel alarm corresponding to pi.
+ if (pi != null) {
+ mAlarmManager.cancel(pi);
+ }
+
// Record caller.
recordCallerLocked(r);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index fcee63c62ae8..4cb6c3ba20d8 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -659,7 +659,8 @@ public class ZenModeHelper {
mConfig.manualRule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS);
}
for (ZenRule rule : mConfig.automaticRules.values()) {
- if (rule.enabled && rule.condition.state == Condition.STATE_TRUE
+ if (rule.enabled && rule.condition != null
+ && rule.condition.state == Condition.STATE_TRUE
&& !rule.snoozing) {
rule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index db2b166aac63..219fa3ca5270 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -134,7 +134,6 @@ import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTi
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive;
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
-import static com.android.server.pm.parsing.PackageInfoUtils.checkUseInstalledOrHidden;
import android.Manifest;
import android.annotation.AppIdInt;
@@ -2516,8 +2515,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
- PackageSetting ps = a == null ? null : mSettings.getPackageLPr(a.getPackageName());
if (pkg != null && mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) {
+ PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
if (ps == null) return null;
if (shouldFilterApplicationLocked(
ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
@@ -2527,8 +2526,8 @@ public class PackageManagerService extends IPackageManager.Stub
a, flags, ps.readUserState(userId), userId, ps);
}
if (resolveComponentName().equals(component)) {
- return generateDelegateActivityInfo(pkg, ps, new PackageUserState(),
- mResolveActivity, flags, userId);
+ return PackageParser.generateActivityInfo(
+ mResolveActivity, flags, new PackageUserState(), userId);
}
return null;
}
@@ -3174,8 +3173,8 @@ public class PackageManagerService extends IPackageManager.Stub
return result;
}
final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
- ephemeralInstaller.activityInfo = generateDelegateActivityInfo(ps.getPkg(), ps,
- ps.readUserState(userId), instantAppInstallerActivity(), 0 /*flags*/, userId);
+ ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo(
+ instantAppInstallerActivity(), 0, ps.readUserState(userId), userId);
ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
// add a non-generic filter
@@ -3259,7 +3258,7 @@ public class PackageManagerService extends IPackageManager.Stub
ai.flags = ps.pkgFlags;
ai.privateFlags = ps.pkgPrivateFlags;
pi.applicationInfo =
- PackageInfoUtils.generateApplicationInfo(p, flags, state, userId, ps);
+ PackageParser.generateApplicationInfo(ai, flags, state, userId);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "ps.pkg is n/a for ["
+ ps.name + "]. Provides a minimum info.");
@@ -3375,19 +3374,6 @@ public class PackageManagerService extends IPackageManager.Stub
return getInstalledPackagesBody(flags, userId, callingUid);
}
- private static ActivityInfo generateDelegateActivityInfo(@Nullable AndroidPackage pkg,
- @Nullable PackageSetting ps, @NonNull PackageUserState state,
- @Nullable ActivityInfo activity, int flags, int userId) {
- if (activity == null || pkg == null
- || !checkUseInstalledOrHidden(pkg, ps, state, flags)) {
- return null;
- }
- final ActivityInfo info = new ActivityInfo(activity);
- info.applicationInfo =
- PackageInfoUtils.generateApplicationInfo(pkg, flags, state, userId, ps);
- return info;
- }
-
public ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
int callingUid) {
// writer
@@ -23672,7 +23658,7 @@ public class PackageManagerService extends IPackageManager.Stub
boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
mContext.getContentResolver(),
android.provider.Settings.Global.COMPATIBILITY_MODE, 1) == 1;
- ParsingPackageUtils.setCompatibilityModeEnabled(compatibilityModeEnabled);
+ PackageParser.setCompatibilityModeEnabled(compatibilityModeEnabled);
if (DEBUG_SETTINGS) {
Log.d(TAG, "compatibility mode:" + compatibilityModeEnabled);
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index b89dbdc863e0..61f51e36202c 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -417,7 +417,7 @@ public class PackageInfoUtils {
* Returns true if the package is installed and not hidden, or if the caller
* explicitly wanted all uninstalled and hidden packages as well.
*/
- public static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
+ private static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
PackageSetting pkgSetting, PackageUserState state,
@PackageManager.PackageInfoFlags int flags) {
// Returns false if the package is hidden system app until installed.
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 884bbea2eb28..1bfa72f64d8e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -70,7 +70,9 @@ import android.app.admin.DevicePolicyManagerInternal;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.AttributionSource;
+import android.content.AttributionSourceState;
import android.content.Context;
+import android.content.PermissionChecker;
import android.content.pm.ApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
@@ -104,6 +106,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.permission.IOnPermissionsChangeListener;
+import android.permission.IPermissionChecker;
import android.permission.IPermissionManager;
import android.permission.PermissionControllerManager;
import android.permission.PermissionManager;
@@ -164,6 +167,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -451,6 +455,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (permissionService == null) {
permissionService = new PermissionManagerService(context, availableFeatures);
ServiceManager.addService("permissionmgr", permissionService);
+ ServiceManager.addService("permission_checker", new PermissionCheckerService(context));
}
return LocalServices.getService(PermissionManagerServiceInternal.class);
}
@@ -2175,23 +2180,30 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
final int callingUid = Binder.getCallingUid();
- final int userId = UserHandle.getUserId(newPackage.getUid());
- int numRequestedPermissions = newPackage.getRequestedPermissions().size();
- for (int i = 0; i < numRequestedPermissions; i++) {
- PermissionInfo permInfo = getPermissionInfo(newPackage.getRequestedPermissions().get(i),
- newPackage.getPackageName(), 0);
- if (permInfo == null || !STORAGE_PERMISSIONS.contains(permInfo.name)) {
- continue;
- }
+ for (int userId: getAllUserIds()) {
+ int numRequestedPermissions = newPackage.getRequestedPermissions().size();
+ for (int i = 0; i < numRequestedPermissions; i++) {
+ PermissionInfo permInfo = getPermissionInfo(
+ newPackage.getRequestedPermissions().get(i),
+ newPackage.getPackageName(), 0);
+ if (permInfo == null || !STORAGE_PERMISSIONS.contains(permInfo.name)) {
+ continue;
+ }
- EventLog.writeEvent(0x534e4554, "171430330", newPackage.getUid(),
- "Revoking permission " + permInfo.name + " from package "
- + newPackage.getPackageName() + " as either the sdk downgraded "
- + downgradedSdk + " or newly requested legacy full storage "
- + newlyRequestsLegacy);
+ EventLog.writeEvent(0x534e4554, "171430330", newPackage.getUid(),
+ "Revoking permission " + permInfo.name + " from package "
+ + newPackage.getPackageName() + " as either the sdk downgraded "
+ + downgradedSdk + " or newly requested legacy full storage "
+ + newlyRequestsLegacy);
- revokeRuntimePermissionInternal(newPackage.getPackageName(), permInfo.name,
- false, callingUid, userId, null, mDefaultPermissionCallback);
+ try {
+ revokeRuntimePermissionInternal(newPackage.getPackageName(), permInfo.name,
+ false, callingUid, userId, null, mDefaultPermissionCallback);
+ } catch (IllegalStateException | SecurityException e) {
+ Log.e(TAG, "unable to revoke " + permInfo.name + " for "
+ + newPackage.getPackageName() + " user " + userId, e);
+ }
+ }
}
}
@@ -5448,4 +5460,419 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
}
+
+ /**
+ * TODO: We need to consolidate these APIs either on PermissionManager or an extension
+ * object or a separate PermissionChecker service in context. The impartant part is to
+ * keep a single impl that is exposed to Java and native. We are not sure about the
+ * API shape so let is soak a bit.
+ */
+ private static final class PermissionCheckerService extends IPermissionChecker.Stub {
+ // Cache for platform defined runtime permissions to avoid multi lookup (name -> info)
+ private static final ConcurrentHashMap<String, PermissionInfo> sPlatformPermissions
+ = new ConcurrentHashMap<>();
+
+ private final @NonNull Context mContext;
+ private final @NonNull AppOpsManager mAppOpsManager;
+
+ PermissionCheckerService(@NonNull Context context) {
+ mContext = context;
+ mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+ }
+
+ @Override
+ @PermissionChecker.PermissionResult
+ public int checkPermission(@NonNull String permission,
+ @NonNull AttributionSourceState attributionSourceState, @Nullable String message,
+ boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource) {
+ Objects.requireNonNull(permission);
+ Objects.requireNonNull(attributionSourceState);
+ final AttributionSource attributionSource = new AttributionSource(
+ attributionSourceState);
+ final int result = checkPermission(mContext, permission, attributionSource, message,
+ forDataDelivery, startDataDelivery, fromDatasource);
+ // Finish any started op if some step in the attribution chain failed.
+ if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED) {
+ finishDataDelivery(AppOpsManager.permissionToOp(permission),
+ attributionSource.asState());
+ }
+ return result;
+ }
+
+ @Override
+ public void finishDataDelivery(@NonNull String op,
+ @NonNull AttributionSourceState attributionSourceState) {
+ if (op == null || attributionSourceState.packageName == null) {
+ return;
+ }
+ mAppOpsManager.finishProxyOp(op, new AttributionSource(attributionSourceState));
+ if (attributionSourceState.next != null) {
+ finishDataDelivery(op, attributionSourceState.next[0]);
+ }
+ }
+
+ @Override
+ @PermissionChecker.PermissionResult
+ public int checkOp(int op, AttributionSourceState attributionSource,
+ String message, boolean forDataDelivery, boolean startDataDelivery) {
+ int result = checkOp(mContext, op, new AttributionSource(attributionSource), message,
+ forDataDelivery, startDataDelivery);
+ if (result != PermissionChecker.PERMISSION_GRANTED && startDataDelivery) {
+ // Finish any started op if some step in the attribution chain failed.
+ finishDataDelivery(AppOpsManager.opToName(op), attributionSource);
+ }
+ return result;
+ }
+
+ @PermissionChecker.PermissionResult
+ private static int checkPermission(@NonNull Context context, @NonNull String permission,
+ @NonNull AttributionSource attributionSource, @Nullable String message,
+ boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource) {
+ PermissionInfo permissionInfo = sPlatformPermissions.get(permission);
+
+ if (permissionInfo == null) {
+ try {
+ permissionInfo = context.getPackageManager().getPermissionInfo(permission, 0);
+ if (PLATFORM_PACKAGE_NAME.equals(permissionInfo.packageName)) {
+ // Double addition due to concurrency is fine - the backing
+ // store is concurrent.
+ sPlatformPermissions.put(permission, permissionInfo);
+ }
+ } catch (PackageManager.NameNotFoundException ignored) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+ }
+
+ if (permissionInfo.isAppOp()) {
+ return checkAppOpPermission(context, permission, attributionSource, message,
+ forDataDelivery, fromDatasource);
+ }
+ if (permissionInfo.isRuntime()) {
+ return checkRuntimePermission(context, permission, attributionSource, message,
+ forDataDelivery, startDataDelivery, fromDatasource);
+ }
+
+ if (!fromDatasource && !checkPermission(context, permission, attributionSource.getUid(),
+ attributionSource.getRenouncedPermissions())) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ if (attributionSource.getNext() != null) {
+ return checkPermission(context, permission,
+ attributionSource.getNext(), message, forDataDelivery,
+ startDataDelivery, /*fromDatasource*/ false);
+ }
+
+ return PermissionChecker.PERMISSION_GRANTED;
+ }
+
+ @PermissionChecker.PermissionResult
+ private static int checkAppOpPermission(@NonNull Context context,
+ @NonNull String permission, @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean forDataDelivery, boolean fromDatasource) {
+ final int op = AppOpsManager.permissionToOpCode(permission);
+ if (op < 0) {
+ Slog.wtf(LOG_TAG, "Appop permission " + permission + " with no app op defined!");
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ AttributionSource current = attributionSource;
+ AttributionSource next = null;
+
+ while (true) {
+ final boolean skipCurrentChecks = (fromDatasource || next != null);
+
+ next = current.getNext();
+
+ // If the call is from a datasource we need to vet only the chain before it. This
+ // way we can avoid the datasource creating an attribution context for every call.
+ if (!(fromDatasource && current == attributionSource)
+ && next != null && !current.isTrusted(context)) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ // The access is for oneself if this is the single receiver of data
+ // after the data source or if this is the single attribution source
+ // in the chain if not from a datasource.
+ final boolean singleReceiverFromDatasource = (fromDatasource
+ && current == attributionSource && next != null && next.getNext() == null);
+ final boolean selfAccess = singleReceiverFromDatasource || next == null;
+
+ final int opMode = performOpTransaction(context, op, current, message,
+ forDataDelivery, /*startDataDelivery*/ false, skipCurrentChecks,
+ selfAccess, singleReceiverFromDatasource);
+
+ switch (opMode) {
+ case AppOpsManager.MODE_IGNORED:
+ case AppOpsManager.MODE_ERRORED: {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+ case AppOpsManager.MODE_DEFAULT: {
+ if (!skipCurrentChecks && !checkPermission(context, permission,
+ attributionSource.getUid(), attributionSource
+ .getRenouncedPermissions())) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+ if (next != null && !checkPermission(context, permission,
+ next.getUid(), next.getRenouncedPermissions())) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+ }
+ }
+
+ if (next == null || next.getNext() == null) {
+ return PermissionChecker.PERMISSION_GRANTED;
+ }
+
+ current = next;
+ }
+ }
+
+ private static int checkRuntimePermission(@NonNull Context context,
+ @NonNull String permission, @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean forDataDelivery, boolean startDataDelivery,
+ boolean fromDatasource) {
+ // Now let's check the identity chain...
+ final int op = AppOpsManager.permissionToOpCode(permission);
+
+ AttributionSource current = attributionSource;
+ AttributionSource next = null;
+
+ while (true) {
+ final boolean skipCurrentChecks = (fromDatasource || next != null);
+ next = current.getNext();
+
+ // If the call is from a datasource we need to vet only the chain before it. This
+ // way we can avoid the datasource creating an attribution context for every call.
+ if (!(fromDatasource && current == attributionSource)
+ && next != null && !current.isTrusted(context)) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ // If we already checked the permission for this one, skip the work
+ if (!skipCurrentChecks && !checkPermission(context, permission,
+ current.getUid(), current.getRenouncedPermissions())) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ if (next != null && !checkPermission(context, permission,
+ next.getUid(), next.getRenouncedPermissions())) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ if (op < 0) {
+ // Bg location is one-off runtime modifier permission and has no app op
+ if (sPlatformPermissions.contains(permission)
+ && !Manifest.permission.ACCESS_BACKGROUND_LOCATION.equals(permission)) {
+ Slog.wtf(LOG_TAG, "Platform runtime permission " + permission
+ + " with no app op defined!");
+ }
+ if (next == null) {
+ return PermissionChecker.PERMISSION_GRANTED;
+ }
+ current = next;
+ continue;
+ }
+
+ // The access is for oneself if this is the single receiver of data
+ // after the data source or if this is the single attribution source
+ // in the chain if not from a datasource.
+ final boolean singleReceiverFromDatasource = (fromDatasource
+ && current == attributionSource && next != null && next.getNext() == null);
+ final boolean selfAccess = singleReceiverFromDatasource || next == null;
+
+ final int opMode = performOpTransaction(context, op, current, message,
+ forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess,
+ singleReceiverFromDatasource);
+
+ switch (opMode) {
+ case AppOpsManager.MODE_ERRORED: {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+ case AppOpsManager.MODE_IGNORED: {
+ return PermissionChecker.PERMISSION_SOFT_DENIED;
+ }
+ }
+
+ if (next == null || next.getNext() == null) {
+ return PermissionChecker.PERMISSION_GRANTED;
+ }
+
+ current = next;
+ }
+ }
+
+ private static boolean checkPermission(@NonNull Context context, @NonNull String permission,
+ int uid, @NonNull Set<String> renouncedPermissions) {
+ final boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1,
+ uid) == PackageManager.PERMISSION_GRANTED;
+ if (permissionGranted && renouncedPermissions.contains(permission)
+ && context.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS,
+ /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ return permissionGranted;
+ }
+
+ private static int checkOp(@NonNull Context context, @NonNull int op,
+ @NonNull AttributionSource attributionSource, @Nullable String message,
+ boolean forDataDelivery, boolean startDataDelivery) {
+ if (op < 0 || attributionSource.getPackageName() == null) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ AttributionSource current = attributionSource;
+ AttributionSource next = null;
+
+ while (true) {
+ final boolean skipCurrentChecks = (next != null);
+ next = current.getNext();
+
+ // If the call is from a datasource we need to vet only the chain before it. This
+ // way we can avoid the datasource creating an attribution context for every call.
+ if (next != null && !current.isTrusted(context)) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ // The access is for oneself if this is the single attribution source in the chain.
+ final boolean selfAccess = (next == null);
+
+ final int opMode = performOpTransaction(context, op, current, message,
+ forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess,
+ /*fromDatasource*/ false);
+
+ switch (opMode) {
+ case AppOpsManager.MODE_ERRORED: {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+ case AppOpsManager.MODE_IGNORED: {
+ return PermissionChecker.PERMISSION_SOFT_DENIED;
+ }
+ }
+
+ if (next == null || next.getNext() == null) {
+ return PermissionChecker.PERMISSION_GRANTED;
+ }
+
+ current = next;
+ }
+ }
+
+ private static int performOpTransaction(@NonNull Context context, int op,
+ @NonNull AttributionSource attributionSource, @Nullable String message,
+ boolean forDataDelivery, boolean startDataDelivery, boolean skipProxyOperation,
+ boolean selfAccess, boolean singleReceiverFromDatasource) {
+ // We cannot perform app ops transactions without a package name. In all relevant
+ // places we pass the package name but just in case there is a bug somewhere we
+ // do a best effort to resolve the package from the UID (pick first without a loss
+ // of generality - they are in the same security sandbox).
+ final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ final AttributionSource accessorSource = (!singleReceiverFromDatasource)
+ ? attributionSource : attributionSource.getNext();
+ if (!forDataDelivery) {
+ final String resolvedAccessorPackageName = resolvePackageName(context,
+ accessorSource);
+ if (resolvedAccessorPackageName == null) {
+ return AppOpsManager.MODE_ERRORED;
+ }
+ final int opMode = appOpsManager.unsafeCheckOpRawNoThrow(op,
+ accessorSource.getUid(), resolvedAccessorPackageName);
+ final AttributionSource next = accessorSource.getNext();
+ if (!selfAccess && opMode == AppOpsManager.MODE_ALLOWED && next != null) {
+ final String resolvedNextPackageName = resolvePackageName(context, next);
+ if (resolvedNextPackageName == null) {
+ return AppOpsManager.MODE_ERRORED;
+ }
+ return appOpsManager.unsafeCheckOpRawNoThrow(op, next.getUid(),
+ resolvedNextPackageName);
+ }
+ return opMode;
+ } else if (startDataDelivery) {
+ final AttributionSource resolvedAttributionSource = resolveAttributionSource(
+ context, accessorSource);
+ if (resolvedAttributionSource.getPackageName() == null) {
+ return AppOpsManager.MODE_ERRORED;
+ }
+ if (selfAccess) {
+ // If the datasource is not in a trusted platform component then in would not
+ // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that
+ // an app is exposing runtime permission protected data but cannot blame others
+ // in a trusted way which would not properly show in permission usage UIs.
+ // As a fallback we note a proxy op that blames the app and the datasource.
+ try {
+ return appOpsManager.startOpNoThrow(op, resolvedAttributionSource.getUid(),
+ resolvedAttributionSource.getPackageName(),
+ /*startIfModeDefault*/ false,
+ resolvedAttributionSource.getAttributionTag(),
+ message);
+ } catch (SecurityException e) {
+ Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with"
+ + " platform defined runtime permission "
+ + AppOpsManager.opToPermission(op) + " while not having "
+ + Manifest.permission.UPDATE_APP_OPS_STATS);
+ return appOpsManager.startProxyOpNoThrow(op, attributionSource, message,
+ skipProxyOperation);
+ }
+ } else {
+ return appOpsManager.startProxyOpNoThrow(op, resolvedAttributionSource, message,
+ skipProxyOperation);
+ }
+ } else {
+ final AttributionSource resolvedAttributionSource = resolveAttributionSource(
+ context, accessorSource);
+ if (resolvedAttributionSource.getPackageName() == null) {
+ return AppOpsManager.MODE_ERRORED;
+ }
+ if (selfAccess) {
+ // If the datasource is not in a trusted platform component then in would not
+ // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that
+ // an app is exposing runtime permission protected data but cannot blame others
+ // in a trusted way which would not properly show in permission usage UIs.
+ // As a fallback we note a proxy op that blames the app and the datasource.
+ try {
+ return appOpsManager.noteOpNoThrow(op, resolvedAttributionSource.getUid(),
+ resolvedAttributionSource.getPackageName(),
+ resolvedAttributionSource.getAttributionTag(),
+ message);
+ } catch (SecurityException e) {
+ Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with"
+ + " platform defined runtime permission "
+ + AppOpsManager.opToPermission(op) + " while not having "
+ + Manifest.permission.UPDATE_APP_OPS_STATS);
+ return appOpsManager.noteProxyOpNoThrow(op, attributionSource, message,
+ skipProxyOperation);
+ }
+ } else {
+ return appOpsManager.noteProxyOpNoThrow(op, resolvedAttributionSource, message,
+ skipProxyOperation);
+ }
+ }
+ }
+
+ private static @Nullable String resolvePackageName(@NonNull Context context,
+ @NonNull AttributionSource attributionSource) {
+ if (attributionSource.getPackageName() != null) {
+ return attributionSource.getPackageName();
+ }
+ final String[] packageNames = context.getPackageManager().getPackagesForUid(
+ attributionSource.getUid());
+ if (packageNames != null) {
+ // This is best effort if the caller doesn't pass a package. The security
+ // sandbox is UID, therefore we pick an arbitrary package.
+ return packageNames[0];
+ }
+ // Last resort to handle special UIDs like root, etc.
+ return AppOpsManager.resolvePackageName(attributionSource.getUid(),
+ attributionSource.getPackageName());
+ }
+
+ private static @NonNull AttributionSource resolveAttributionSource(
+ @NonNull Context context, @NonNull AttributionSource attributionSource) {
+ if (attributionSource.getPackageName() != null) {
+ return attributionSource;
+ }
+ return attributionSource.withPackageName(resolvePackageName(context,
+ attributionSource));
+ }
+ }
}
diff --git a/services/core/java/com/android/server/sensors/SensorService.java b/services/core/java/com/android/server/sensors/SensorService.java
new file mode 100644
index 000000000000..f7ed8e7138fa
--- /dev/null
+++ b/services/core/java/com/android/server/sensors/SensorService.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 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.sensors;
+
+import android.content.Context;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ConcurrentUtils;
+import com.android.server.SystemServerInitThreadPool;
+import com.android.server.SystemService;
+import com.android.server.utils.TimingsTraceAndSlog;
+
+import java.util.concurrent.Future;
+
+public class SensorService extends SystemService {
+ private static final String START_NATIVE_SENSOR_SERVICE = "StartNativeSensorService";
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private Future<?> mSensorServiceStart;
+
+
+ /** Start the sensor service. This is a blocking call and can take time. */
+ private static native void startNativeSensorService();
+
+ public SensorService(Context ctx) {
+ super(ctx);
+ synchronized (mLock) {
+ mSensorServiceStart = SystemServerInitThreadPool.submit(() -> {
+ TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
+ traceLog.traceBegin(START_NATIVE_SENSOR_SERVICE);
+ startNativeSensorService();
+ traceLog.traceEnd();
+ }, START_NATIVE_SENSOR_SERVICE);
+ }
+ }
+
+ @Override
+ public void onStart() { }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE) {
+ ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart,
+ START_NATIVE_SENSOR_SERVICE);
+ synchronized (mLock) {
+ mSensorServiceStart = null;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 6e0efbf8bd51..ba188937cfea 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -488,6 +488,12 @@ class SurfaceAnimator {
public static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6;
/**
+ * Animation when a reveal starting window animation is applied to app window.
+ * @hide
+ */
+ public static final int ANIMATION_TYPE_STARTING_REVEAL = 1 << 7;
+
+ /**
* Bitmask to include all animation types. This is NOT an {@link AnimationType}
* @hide
*/
@@ -505,7 +511,8 @@ class SurfaceAnimator {
ANIMATION_TYPE_RECENTS,
ANIMATION_TYPE_WINDOW_ANIMATION,
ANIMATION_TYPE_INSETS_CONTROL,
- ANIMATION_TYPE_FIXED_TRANSFORM
+ ANIMATION_TYPE_FIXED_TRANSFORM,
+ ANIMATION_TYPE_STARTING_REVEAL
})
@Retention(RetentionPolicy.SOURCE)
@interface AnimationType {}
@@ -523,6 +530,7 @@ class SurfaceAnimator {
case ANIMATION_TYPE_WINDOW_ANIMATION: return "window_animation";
case ANIMATION_TYPE_INSETS_CONTROL: return "insets_animation";
case ANIMATION_TYPE_FIXED_TRANSFORM: return "fixed_rotation";
+ case ANIMATION_TYPE_STARTING_REVEAL: return "starting_reveal";
default: return "unknown type:" + type;
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2c592d00f90c..e5e1a7a245d6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4309,21 +4309,11 @@ class Task extends WindowContainer<WindowContainer> {
// the screen are opaque.
return TASK_VISIBILITY_INVISIBLE;
}
- if (gotRootSplitScreenTask) {
- if (isAssistantType) {
- // Assistant stack can't be visible behind split-screen. In addition to this not
- // making sense, it also works around an issue here we boost the z-order of the
- // assistant window surfaces in window manager whenever it is visible.
- return TASK_VISIBILITY_INVISIBLE;
- }
- if (other.isHomeOrRecentsRootTask()) {
- // While in split mode, home task will be reparented to the secondary split and
- // leaving tasks not supporting split below. Due to
- // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to
- // the bottom, this makes sure those tasks below home is invisible and won't
- // occlude home task unexpectedly.
- return TASK_VISIBILITY_INVISIBLE;
- }
+ if (isAssistantType && gotRootSplitScreenTask) {
+ // Assistant stack can't be visible behind split-screen. In addition to this not
+ // making sense, it also works around an issue here we boost the z-order of the
+ // assistant window surfaces in window manager whenever it is visible.
+ return TASK_VISIBILITY_INVISIBLE;
}
if (other.mAdjacentTask != null) {
if (adjacentTasks.contains(other.mAdjacentTask)) {
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index ccc0916cf25b..fb481b41c0af 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_STARTING_REVEAL;
import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS;
import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS;
@@ -38,6 +39,7 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
import android.window.ITaskOrganizerController;
@@ -134,29 +136,72 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
+ // Capture the animation surface control for activity's main window
+ private class StartingWindowAnimationAdaptor implements AnimationAdapter {
+ private SurfaceControl mAnimationLeash;
+ @Override
+ public boolean getShowWallpaper() {
+ return false;
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
+ int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
+ mAnimationLeash = animationLeash;
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {
+ if (mAnimationLeash == animationLeash) {
+ mAnimationLeash = null;
+ }
+ }
+
+ @Override
+ public long getDurationHint() {
+ return 0;
+ }
+
+ @Override
+ public long getStatusBarTransitionsStartTime() {
+ return 0;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix + "StartingWindowAnimationAdaptor mCapturedLeash=");
+ pw.print(mAnimationLeash);
+ pw.println();
+ }
+
+ @Override
+ public void dumpDebug(ProtoOutputStream proto) {
+ }
+ }
+
void removeStartingWindow(Task task, boolean prepareAnimation) {
- SurfaceControl firstWindowLeash = null;
+ SurfaceControl windowAnimationLeash = null;
Rect mainFrame = null;
- // TODO enable shift up animation once we fix flicker test
-// final boolean playShiftUpAnimation = !task.inMultiWindowMode();
-// if (prepareAnimation && playShiftUpAnimation) {
-// final ActivityRecord topActivity = task.topActivityWithStartingWindow();
-// if (topActivity != null) {
-// final WindowState mainWindow =
-// topActivity.findMainWindow(false/* includeStartingApp */);
-// if (mainWindow != null) {
- // TODO create proper leash instead of the copied SC
-// firstWindowLeash = new SurfaceControl(mainWindow.getSurfaceControl(),
-// "TaskOrganizerController.removeStartingWindow");
-// mainFrame = mainWindow.getRelativeFrame();
-// }
-// }
-// }
+ final boolean playShiftUpAnimation = !task.inMultiWindowMode();
+ if (prepareAnimation && playShiftUpAnimation) {
+ final ActivityRecord topActivity = task.topActivityWithStartingWindow();
+ if (topActivity != null) {
+ final WindowState mainWindow =
+ topActivity.findMainWindow(false/* includeStartingApp */);
+ if (mainWindow != null) {
+ final StartingWindowAnimationAdaptor adaptor =
+ new StartingWindowAnimationAdaptor();
+ final SurfaceControl.Transaction t = mainWindow.getPendingTransaction();
+ mainWindow.startAnimation(t, adaptor, false,
+ ANIMATION_TYPE_STARTING_REVEAL);
+ windowAnimationLeash = adaptor.mAnimationLeash;
+ mainFrame = mainWindow.getRelativeFrame();
+ }
+ }
+ }
try {
- mTaskOrganizer.removeStartingWindow(task.mTaskId, firstWindowLeash, mainFrame,
- /* TODO(183004107) Revert this when jankiness is solved
- prepareAnimation); */ false);
-
+ mTaskOrganizer.removeStartingWindow(task.mTaskId, windowAnimationLeash,
+ mainFrame, prepareAnimation);
} catch (RemoteException e) {
Slog.e(TAG, "Exception sending onStartTaskFinished callback", e);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2e8d4cd4e7f8..b40223f6eed7 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -133,6 +133,7 @@ import static com.android.server.wm.MoveAnimationSpecProto.TO;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_STARTING_REVEAL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -2410,6 +2411,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
ProtoLog.d(WM_DEBUG_STARTING_WINDOW, "Starting window removed %s", this);
}
+ if (startingWindow && StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
+ // cancel the remove starting window animation on shell
+ if (mActivityRecord != null) {
+ final WindowState mainWindow =
+ mActivityRecord.findMainWindow(false/* includeStartingApp */);
+ if (mainWindow != null && mainWindow.isSelfAnimating(0 /* flags */,
+ ANIMATION_TYPE_STARTING_REVEAL)) {
+ mainWindow.cancelAnimation();
+ }
+ }
+ }
+
ProtoLog.v(WM_DEBUG_FOCUS, "Remove client=%x, surfaceController=%s Callers=%s",
System.identityHashCode(mClient.asBinder()),
mWinAnimator.mSurfaceController,
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 4ce959180753..53401fd47178 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -70,6 +70,7 @@ cc_library_static {
"com_android_server_PersistentDataBlockService.cpp",
"com_android_server_am_LowMemDetector.cpp",
"com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
+ "com_android_server_sensor_SensorService.cpp",
"onload.cpp",
":lib_cachedAppOptimizer_native",
":lib_networkStatsFactory_native",
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index fb664abb2d54..6721ecfc40a1 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -32,7 +32,6 @@
#include <memtrackproxy/MemtrackProxy.h>
#include <schedulerservice/SchedulingPolicyService.h>
-#include <sensorservice/SensorService.h>
#include <sensorservicehidl/SensorManager.h>
#include <stats/StatsAidl.h>
#include <stats/StatsHal.h>
@@ -41,12 +40,10 @@
#include <bionic/reserved_signals.h>
#include <android-base/properties.h>
-#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/misc.h>
#include <utils/AndroidThreads.h>
-using android::base::GetIntProperty;
using namespace std::chrono_literals;
namespace {
@@ -81,15 +78,6 @@ static void android_server_SystemServer_startIStatsService(JNIEnv* /* env */, jo
startStatsAidlService();
}
-static void android_server_SystemServer_startSensorService(JNIEnv* /* env */, jobject /* clazz */) {
- char propBuf[PROPERTY_VALUE_MAX];
- property_get("system_init.startsensorservice", propBuf, "1");
- if (strcmp(propBuf, "1") == 0) {
- SensorService::publish(false /* allowIsolated */,
- IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL);
- }
-}
-
static void android_server_SystemServer_startMemtrackProxyService(JNIEnv* env,
jobject /* clazz */) {
using aidl::android::hardware::memtrack::MemtrackProxy;
@@ -163,7 +151,6 @@ static void android_server_SystemServer_setIncrementalServiceSystemReady(JNIEnv*
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{"startIStatsService", "()V", (void*)android_server_SystemServer_startIStatsService},
- {"startSensorService", "()V", (void*)android_server_SystemServer_startSensorService},
{"startMemtrackProxyService", "()V",
(void*)android_server_SystemServer_startMemtrackProxyService},
{"startHidlServices", "()V", (void*)android_server_SystemServer_startHidlServices},
diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp
new file mode 100644
index 000000000000..acad1bc95ce0
--- /dev/null
+++ b/services/core/jni/com_android_server_sensor_SensorService.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#define LOG_TAG "SensorService"
+
+#include <nativehelper/JNIHelp.h>
+#include "android_runtime/AndroidRuntime.h"
+#include "core_jni_helpers.h"
+#include "jni.h"
+
+#include <cutils/properties.h>
+#include <sensorservice/SensorService.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+namespace android {
+
+static void startNativeSensorService(JNIEnv* env, jclass clazz) {
+ char propBuf[PROPERTY_VALUE_MAX];
+ property_get("system_init.startsensorservice", propBuf, "1");
+ if (strcmp(propBuf, "1") == 0) {
+ SensorService::publish(false /* allowIsolated */,
+ IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL);
+ }
+}
+
+static const JNINativeMethod methods[] = {
+ {"startNativeSensorService", "()V", (void*)startNativeSensorService},
+};
+
+int register_android_server_sensor_SensorService(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "com/android/server/sensors/SensorService", methods,
+ NELEM(methods));
+}
+
+}; // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index b043e643b2bd..b8961d5ba9b0 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -62,6 +62,7 @@ int register_android_server_AdbDebuggingManager(JNIEnv* env);
int register_android_server_FaceService(JNIEnv* env);
int register_android_server_GpuService(JNIEnv* env);
int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
+int register_android_server_sensor_SensorService(JNIEnv* env);
};
using namespace android;
@@ -117,5 +118,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_FaceService(env);
register_android_server_GpuService(env);
register_android_server_stats_pull_StatsPullAtomService(env);
+ register_android_server_sensor_SensorService(env);
return JNI_VERSION_1_4;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8dc501119af1..1e4b2485cd94 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -179,6 +179,7 @@ import com.android.server.rotationresolver.RotationResolverManagerService;
import com.android.server.security.FileIntegrityService;
import com.android.server.security.KeyAttestationApplicationIdProviderService;
import com.android.server.security.KeyChainSystemService;
+import com.android.server.sensors.SensorService;
import com.android.server.signedconfig.SignedConfigService;
import com.android.server.soundtrigger.SoundTriggerService;
import com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareService;
@@ -427,7 +428,6 @@ public final class SystemServer implements Dumpable {
private final long mRuntimeStartElapsedTime;
private final long mRuntimeStartUptime;
- private static final String START_SENSOR_SERVICE = "StartSensorService";
private static final String START_HIDL_SERVICES = "StartHidlServices";
private static final String START_BLOB_STORE_SERVICE = "startBlobStoreManagerService";
@@ -435,7 +435,6 @@ public final class SystemServer implements Dumpable {
private static final String SYSPROP_START_ELAPSED = "sys.system_server.start_elapsed";
private static final String SYSPROP_START_UPTIME = "sys.system_server.start_uptime";
- private Future<?> mSensorServiceStart;
private Future<?> mZygotePreload;
private Future<?> mBlobStoreServiceStart;
@@ -449,9 +448,6 @@ public final class SystemServer implements Dumpable {
/** Start the IStats services. This is a blocking call and can take time. */
private static native void startIStatsService();
- /** Start the sensor service. This is a blocking call and can take time. */
- private static native void startSensorService();
-
/**
* Start the memtrack proxy service.
*/
@@ -1229,15 +1225,9 @@ public final class SystemServer implements Dumpable {
// The sensor service needs access to package manager service, app ops
// service, and permissions service, therefore we start it after them.
- // Start sensor service in a separate thread. Completion should be checked
- // before using it.
- mSensorServiceStart = SystemServerInitThreadPool.submit(() -> {
- TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
- traceLog.traceBegin(START_SENSOR_SERVICE);
- startSensorService();
- traceLog.traceEnd();
- }, START_SENSOR_SERVICE);
-
+ t.traceBegin("StartSensorService");
+ mSystemServiceManager.startService(SensorService.class);
+ t.traceEnd();
t.traceEnd(); // startBootstrapServices
}
@@ -1474,8 +1464,7 @@ public final class SystemServer implements Dumpable {
t.traceBegin("StartWindowManagerService");
// WMS needs sensor service ready
- ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
- mSensorServiceStart = null;
+ mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
@@ -1493,8 +1482,8 @@ public final class SystemServer implements Dumpable {
t.traceEnd();
// Start receiving calls from HIDL services. Start in in a separate thread
- // because it need to connect to SensorManager. This have to start
- // after START_SENSOR_SERVICE is done.
+ // because it need to connect to SensorManager. This has to start
+ // after PHASE_WAIT_FOR_SENSOR_SERVICE is done.
SystemServerInitThreadPool.submit(() -> {
TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
traceLog.traceBegin(START_HIDL_SERVICES);
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 8382ff4cb506..72342810c4b6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -129,9 +129,11 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerExemptionManager;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
@@ -369,6 +371,7 @@ public class AlarmManagerServiceTest {
.mockStatic(MetricsHelper.class)
.mockStatic(Settings.Global.class)
.mockStatic(ServiceManager.class)
+ .mockStatic(SystemProperties.class)
.spyStatic(UserHandle.class)
.strictness(Strictness.WARN)
.startMocking();
@@ -2579,6 +2582,24 @@ public class AlarmManagerServiceTest {
verify(() -> MetricsHelper.pushAlarmBatchDelivered(10, 5));
}
+ @Test
+ public void setTimeZoneImpl() {
+ final long durationMs = 20000L;
+ when(mActivityManagerInternal.getBootTimeTempAllowListDuration()).thenReturn(durationMs);
+ mService.setTimeZoneImpl("UTC");
+ final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mMockContext).sendBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL),
+ isNull(), bundleCaptor.capture());
+ assertEquals(Intent.ACTION_TIMEZONE_CHANGED, intentCaptor.getValue().getAction());
+ final BroadcastOptions bOptions = new BroadcastOptions(bundleCaptor.getValue());
+ assertEquals(durationMs, bOptions.getTemporaryAppAllowlistDuration());
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ bOptions.getTemporaryAppAllowlistType());
+ assertEquals(PowerExemptionManager.REASON_TIMEZONE_CHANGED,
+ bOptions.getTemporaryAppAllowlistReasonCode());
+ }
+
@After
public void tearDown() {
if (mMockingSession != null) {
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 41237c8ae51f..12e0d8bc5d62 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -168,7 +168,7 @@ public class ActivityManagerServiceTest {
private void mockNoteOperation() {
SyncNotedAppOp allowed = new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED,
- AppOpsManager.OP_GET_USAGE_STATS, null);
+ AppOpsManager.OP_GET_USAGE_STATS, null, mContext.getPackageName());
when(mAppOpsService.noteOperation(eq(AppOpsManager.OP_GET_USAGE_STATS), eq(Process.myUid()),
nullable(String.class), nullable(String.class), any(Boolean.class),
nullable(String.class), any(Boolean.class))).thenReturn(allowed);
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 4004e37dc4e0..21bb43cb6f32 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -572,6 +572,13 @@ public class SubscriptionInfo implements Parcelable {
/**
* @hide
*/
+ public void clearGroupUuid() {
+ this.mGroupUUID = null;
+ }
+
+ /**
+ * @hide
+ */
public List<String> getEhplmns() {
return mEhplmns == null ? Collections.emptyList() : Arrays.asList(mEhplmns);
}
diff --git a/tests/ActivityViewTest/Android.bp b/tests/ActivityViewTest/Android.bp
deleted file mode 100644
index 95178a0fb9a5..000000000000
--- a/tests/ActivityViewTest/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test {
- name: "ActivityViewTest",
- srcs: ["src/**/*.java"],
- platform_apis: true,
- certificate: "platform",
-}
diff --git a/tests/ActivityViewTest/AndroidManifest.xml b/tests/ActivityViewTest/AndroidManifest.xml
deleted file mode 100644
index 7563a25424ad..000000000000
--- a/tests/ActivityViewTest/AndroidManifest.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.google.android.test.activityview">
- <uses-permission android:name="android.permission.INJECT_EVENTS"/>
- <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
- <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING"/>
- <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
-
- <uses-sdk android:targetSdkVersion="27"/>
- <application android:label="ActivityViewTest">
- <activity android:name=".ActivityViewMainActivity"
- android:label="AV Main"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- </activity>
-
- <activity android:name=".ActivityViewActivity"
- android:label="AV"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
- android:windowSoftInputMode="stateHidden|adjustResize">
- </activity>
-
- <activity android:name=".ActivityViewResizeActivity"
- android:label="AV Resize"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
- android:windowSoftInputMode="stateHidden|adjustResize">
- </activity>
-
- <activity android:name=".ActivityViewScrollActivity"
- android:label="AV Scroll"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
- android:windowSoftInputMode="stateHidden">
- </activity>
-
- <activity android:name=".ActivityViewTestActivity"
- android:resizeableActivity="true"
- android:theme="@*android:style/Theme.NoTitleBar"
- android:exported="true"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
- </activity>
-
- <activity android:name=".ActivityViewVisibilityActivity"
- android:label="AV Visibility"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
- </activity>
- </application>
-</manifest>
diff --git a/tests/ActivityViewTest/res/layout/activity_view_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_activity.xml
deleted file mode 100644
index 67c01f8c78fe..000000000000
--- a/tests/ActivityViewTest/res/layout/activity_view_activity.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?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:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#cfd8dc">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <Button
- android:id="@+id/activity_launch_button"
- android:layout_width="200dp"
- android:layout_height="wrap_content"
- android:text="Launch test activity" />
-
- <Button
- android:id="@+id/activity_pick_launch_button"
- android:layout_width="200dp"
- android:layout_height="wrap_content"
- android:text="Launch from picker" />
-
- </LinearLayout>
-
- <ActivityView
- android:id="@+id/activity_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
-</LinearLayout> \ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml
deleted file mode 100644
index efcaef679a9c..000000000000
--- a/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <Button
- android:id="@+id/activity_view_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Test ActivityView"
- android:textAllCaps="false"/>
-
- <Button
- android:id="@+id/scroll_activity_view_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Test Scroll ActivityView"
- android:textAllCaps="false"/>
-
- <Button
- android:id="@+id/resize_activity_view_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Test Resize ActivityView"
- android:textAllCaps="false"/>
-
- <Button
- android:id="@+id/visibility_activity_view_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Test ActivityView Visibility"
- android:textAllCaps="false"/>
-</LinearLayout>
diff --git a/tests/ActivityViewTest/res/layout/activity_view_resize_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_resize_activity.xml
deleted file mode 100644
index 18d86e3d5a6f..000000000000
--- a/tests/ActivityViewTest/res/layout/activity_view_resize_activity.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?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:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#cfd8dc">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <Button
- android:id="@+id/activity_launch_button"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:text="Launch" />
-
- <Button
- android:id="@+id/activity_resize_button"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:text="Resize" />
- </LinearLayout>
-
- <SeekBar
- android:id="@+id/activity_view_seek_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <ActivityView
- android:id="@+id/activity_view"
- android:layout_width="match_parent"
- android:layout_height="600dp" />
-
-</LinearLayout> \ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_scroll_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_scroll_activity.xml
deleted file mode 100644
index 879c2c20a082..000000000000
--- a/tests/ActivityViewTest/res/layout/activity_view_scroll_activity.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?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:orientation="vertical" android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <Button
- android:id="@+id/activity_launch_button"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:text="Launch" />
-
- <ScrollView
- android:id="@+id/activity_view_host_scroll_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:color="#cfd8dc">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <View
- android:layout_width="match_parent"
- android:layout_height="300dp"
- android:layout_gravity="center_horizontal"
- android:background="#eeeeee" />
-
- <ActivityView
- android:id="@+id/activity_view"
- android:layout_width="match_parent"
- android:layout_height="300dp"
- android:background="#fce4ec" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="300dp"
- android:layout_gravity="center_horizontal"
- android:background="#eeeeee" />
- </LinearLayout>
- </ScrollView>
-</LinearLayout> \ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
deleted file mode 100644
index 338d68adfafb..000000000000
--- a/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?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.
--->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/test_activity_root"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#ffe0b2">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:orientation="vertical"
- android:background="#00000000" >
- <TextView
- android:id="@+id/test_activity_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textColor="@android:color/black"
- android:background="#00000000"
- android:gravity="center" />
- <TextView
- android:id="@+id/test_activity_touch_state"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textColor="@android:color/black"
- android:background="#00000000"
- android:gravity="center" />
- </LinearLayout>
-
- <TextView
- android:id="@+id/test_activity_width_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textColor="@android:color/black"
- android:background="#00000000"
- android:gravity="center" />
-
- <TextView
- android:id="@+id/test_activity_height_text"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignParentRight="true"
- android:layout_alignParentEnd="true"
- android:textColor="@android:color/black"
- android:background="#00000000"
- android:gravity="center" />
-
- <EditText
- android:id="@+id/test_activity_edittext"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_margin="16dp" />
-</RelativeLayout> \ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_visibility_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_visibility_activity.xml
deleted file mode 100644
index d29d4dfc0428..000000000000
--- a/tests/ActivityViewTest/res/layout/activity_view_visibility_activity.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#cfd8dc">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <Button
- android:id="@+id/activity_launch_button"
- android:layout_width="200dp"
- android:layout_height="wrap_content"
- android:text="Launch test activity" />
-
- <Spinner
- android:id="@+id/visibility_spinner"
- android:layout_width="200dp"
- android:layout_height="match_parent"/>
-
- </LinearLayout>
-
- <ActivityView
- android:id="@+id/activity_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
-</LinearLayout>
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java
deleted file mode 100644
index f7c60fc73cb3..000000000000
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java
+++ /dev/null
@@ -1,56 +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.google.android.test.activityview;
-
-import android.app.Activity;
-import android.app.ActivityView;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.widget.Button;
-
-public class ActivityViewActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_view_activity);
-
- final ActivityView activityView = findViewById(R.id.activity_view);
- final Button launchButton = findViewById(R.id.activity_launch_button);
- launchButton.setOnClickListener(v -> {
- final Intent intent = new Intent(this, ActivityViewTestActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- activityView.startActivity(intent);
- });
- final Button pickActivityLaunchButton = findViewById(R.id.activity_pick_launch_button);
- pickActivityLaunchButton.setOnClickListener(v -> {
- final Intent intent = Intent.makeMainActivity(null);
- final Intent chooser = Intent.createChooser(intent,
- "Pick an app to launch in ActivityView");
- chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[] {
- new Intent(Intent.ACTION_MAIN)
- .addCategory("com.android.internal.category.PLATLOGO")
- });
- if (intent.resolveActivity(getPackageManager()) != null) {
- chooser.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- activityView.startActivity(chooser);
- }
- });
- }
-}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java
deleted file mode 100644
index 4f09c28fe711..000000000000
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java
+++ /dev/null
@@ -1,42 +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.google.android.test.activityview;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-
-public class ActivityViewMainActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_view_main_activity);
-
- findViewById(R.id.activity_view_button).setOnClickListener(
- v -> startActivity(new Intent(this, ActivityViewActivity.class)));
-
- findViewById(R.id.scroll_activity_view_button).setOnClickListener(
- v -> startActivity(new Intent(this, ActivityViewScrollActivity.class)));
-
- findViewById(R.id.resize_activity_view_button).setOnClickListener(
- v -> startActivity(new Intent(this, ActivityViewResizeActivity.class)));
-
- findViewById(R.id.visibility_activity_view_button).setOnClickListener(
- v -> startActivity(new Intent(this, ActivityViewVisibilityActivity.class)));
- }
-}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewResizeActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewResizeActivity.java
deleted file mode 100644
index 8860a771fd5a..000000000000
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewResizeActivity.java
+++ /dev/null
@@ -1,79 +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.google.android.test.activityview;
-
-import android.app.Activity;
-import android.app.ActivityView;
-import android.content.Intent;
-import android.os.Bundle;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.SeekBar;
-
-public class ActivityViewResizeActivity extends Activity {
- private static final int SMALL_SIZE = 600;
- private static final int LARGE_SIZE = 1200;
-
- private ActivityView mActivityView;
-
- private boolean mFlipSize;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_view_resize_activity);
-
- mActivityView = findViewById(R.id.activity_view);
-
- final Button launchButton = findViewById(R.id.activity_launch_button);
- launchButton.setOnClickListener(v -> {
- final Intent intent = new Intent(this, ActivityViewTestActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- mActivityView.startActivity(intent);
- });
- final Button resizeButton = findViewById(R.id.activity_resize_button);
- if (resizeButton != null) {
- resizeButton.setOnClickListener(v -> {
- LinearLayout.LayoutParams params =
- (LinearLayout.LayoutParams) mActivityView.getLayoutParams();
- params.height = mFlipSize ? SMALL_SIZE : LARGE_SIZE;
- mFlipSize = !mFlipSize;
- mActivityView.setLayoutParams(params);
- });
- }
- final SeekBar seekBar = findViewById(R.id.activity_view_seek_bar);
- if (seekBar != null) {
- seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- final LinearLayout.LayoutParams params =
- (LinearLayout.LayoutParams) mActivityView.getLayoutParams();
- params.height = SMALL_SIZE + progress * 10;
- mActivityView.setLayoutParams(params);
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
- }
- }
-}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewScrollActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewScrollActivity.java
deleted file mode 100644
index 56543662675c..000000000000
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewScrollActivity.java
+++ /dev/null
@@ -1,44 +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.google.android.test.activityview;
-
-import android.app.Activity;
-import android.app.ActivityView;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
-
-public class ActivityViewScrollActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_view_scroll_activity);
-
- final ActivityView activityView = findViewById(R.id.activity_view);
- final Button launchButton = findViewById(R.id.activity_launch_button);
- launchButton.setOnClickListener(v -> {
- final Intent intent = new Intent(this, ActivityViewTestActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- activityView.startActivity(intent);
- });
- findViewById(R.id.activity_view_host_scroll_view).setOnScrollChangeListener(
- (View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY)
- -> activityView.onLocationChanged());
- }
-}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
deleted file mode 100644
index 52aba2b13c42..000000000000
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
+++ /dev/null
@@ -1,116 +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.google.android.test.activityview;
-
-import static android.view.MotionEvent.ACTION_CANCEL;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_MOVE;
-import static android.view.MotionEvent.ACTION_UP;
-
-import android.app.Activity;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.widget.TextView;
-
-public class ActivityViewTestActivity extends Activity {
- private static final String TAG = "ActivityViewTestActivity";
-
- private View mRoot;
- private TextView mTextView;
- private TextView mWidthTextView;
- private TextView mHeightTextView;
- private TextView mTouchStateTextView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_view_test_activity);
- mRoot = findViewById(R.id.test_activity_root);
- mTextView = findViewById(R.id.test_activity_title);
- mWidthTextView = findViewById(R.id.test_activity_width_text);
- mHeightTextView = findViewById(R.id.test_activity_height_text);
- mTouchStateTextView = findViewById(R.id.test_activity_touch_state);
- ViewTreeObserver viewTreeObserver = mRoot.getViewTreeObserver();
- if (viewTreeObserver.isAlive()) {
- viewTreeObserver.addOnGlobalLayoutListener(this::updateDimensionTexts);
- }
- updateStateText("CREATED");
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- updateStateText("STARTED");
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- updateStateText("RESUMED");
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- updateStateText("PAUSED");
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- updateStateText("STOPPED");
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- updateDimensionTexts();
- }
-
- private void updateStateText(String state) {
- Log.d(TAG, state);
- mTextView.setText(state);
- }
-
- private void updateDimensionTexts() {
- mWidthTextView.setText("" + mRoot.getWidth());
- mHeightTextView.setText("" + mRoot.getHeight());
- }
-
- private void updateTouchState(MotionEvent event) {
- switch (event.getAction()) {
- case ACTION_DOWN:
- case ACTION_MOVE:
- mTouchStateTextView.setText("[" + event.getX() + "," + event.getY() + "]");
- break;
- case ACTION_UP:
- case ACTION_CANCEL:
- mTouchStateTextView.setText("");
- break;
- }
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent event) {
- updateTouchState(event);
- return super.dispatchTouchEvent(event);
- }
-}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewVisibilityActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewVisibilityActivity.java
deleted file mode 100644
index ecd2cf3c578e..000000000000
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewVisibilityActivity.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * 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.google.android.test.activityview;
-
-import static android.view.View.GONE;
-import static android.view.View.INVISIBLE;
-import static android.view.View.VISIBLE;
-
-import android.app.Activity;
-import android.app.ActivityView;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.Spinner;
-
-public class ActivityViewVisibilityActivity extends Activity {
- private static final String[] sVisibilityOptions = {"VISIBLE", "INVISIBLE", "GONE"};
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_view_visibility_activity);
-
- final ActivityView activityView = findViewById(R.id.activity_view);
- final Button launchButton = findViewById(R.id.activity_launch_button);
- launchButton.setOnClickListener(v -> {
- final Intent intent = new Intent(this, ActivityViewTestActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- activityView.startActivity(intent);
- });
-
- final Spinner visibilitySpinner = findViewById(R.id.visibility_spinner);
- final ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
- android.R.layout.simple_spinner_item, sVisibilityOptions);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- visibilitySpinner.setAdapter(adapter);
- visibilitySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- switch (position) {
- case 0:
- activityView.setVisibility(VISIBLE);
- break;
- case 1:
- activityView.setVisibility(INVISIBLE);
- break;
- case 2:
- activityView.setVisibility(GONE);
- break;
- }
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
- }
-}
diff --git a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
index 741745560c61..35859fe1472e 100644
--- a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
+++ b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
@@ -48,6 +48,8 @@ public class MultiUserRollbackTest extends BaseHostJUnit4Test {
public void tearDown() throws Exception {
removeSecondaryUserIfNecessary();
runPhaseForUsers("cleanUp", mOriginalUserId);
+ uninstallPackage("com.android.cts.install.lib.testapp.A");
+ uninstallPackage("com.android.cts.install.lib.testapp.B");
}
@Before
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index 9793c3447ff2..898b8d4cbdc1 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -112,7 +112,8 @@ public class UpdatableSystemFontTest {
@After
public void tearDown() throws Exception {
- expectCommandToSucceed("cmd font clear");
+ // Ignore errors because this may fail if updatable system font is not enabled.
+ runShellCommand("cmd font clear", null);
if (mKeyId != null) {
expectCommandToSucceed("mini-keyctl unlink " + mKeyId + " .fs-verity");
}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index aa4b5f8e208f..9ecd82ff6bcb 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -77,6 +77,7 @@ import android.os.test.TestLooper;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.util.ArraySet;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -98,6 +99,7 @@ import java.io.FileNotFoundException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
@@ -326,6 +328,17 @@ public class VcnManagementServiceTest {
return subIdToGroupMap.get(invocation.getArgument(0));
}).when(snapshot).getGroupForSubId(anyInt());
+ doAnswer(invocation -> {
+ final ParcelUuid subGrp = invocation.getArgument(0);
+ final Set<Integer> subIds = new ArraySet<>();
+ for (Entry<Integer, ParcelUuid> entry : subIdToGroupMap.entrySet()) {
+ if (entry.getValue().equals(subGrp)) {
+ subIds.add(entry.getKey());
+ }
+ }
+ return subIds;
+ }).when(snapshot).getAllSubIdsInGroup(any());
+
final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
cb.onNewSnapshot(snapshot);
@@ -914,6 +927,18 @@ public class VcnManagementServiceTest {
verify(mMockPolicyListener).onPolicyChanged();
}
+ @Test
+ public void testVcnSubIdChangeUpdatesPolicyListener() throws Exception {
+ startAndGetVcnInstance(TEST_UUID_2);
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ triggerSubscriptionTrackerCbAndGetSnapshot(
+ Collections.singleton(TEST_UUID_2),
+ Collections.singletonMap(TEST_SUBSCRIPTION_ID, TEST_UUID_2));
+
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
+
private void triggerVcnSafeMode(
@NonNull ParcelUuid subGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,