From b845e08b00d474508d565162611b4cdb89402fad Mon Sep 17 00:00:00 2001 From: RakeshBatra Date: Wed, 9 Sep 2020 12:00:21 +0000 Subject: [PATCH 1/3] Merge tag 'android-10.0.0_r46' of https://android.googlesource.com/platform/frameworks/base into c10 Android 10.0.0 release 46 --- .../android/content/pm/PackageInstaller.java | 2 + .../bugreports/BugreportManagerTest.java | 49 +++++- .../location/GpsNetInitiatedHandler.java | 7 +- .../screenrecord/RecordingService.java | 2 +- .../stack/NotificationStackScrollLayout.java | 4 +- .../systemui/util/leak/LeakReporter.java | 8 +- .../systemui/appops/AppOpsControllerTest.java | 2 + .../NotificationStackScrollLayoutTest.java | 3 +- .../server/am/ActivityManagerService.java | 4 + .../android/server/appop/AppOpsService.java | 20 +-- .../location/GnssVisibilityControl.java | 2 - .../server/pm/PackageInstallerService.java | 20 ++- .../server/pm/PackageInstallerSession.java | 49 +++++- .../server/pm/PackageManagerService.java | 147 ++++++++++++------ .../java/com/android/server/pm/Settings.java | 9 +- .../com/android/server/pm/StagingManager.java | 5 +- .../wallpaper/WallpaperManagerService.java | 1 + 17 files changed, 229 insertions(+), 105 deletions(-) diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index a15caa09bb60..44842c62a3c8 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -2082,6 +2082,7 @@ public long getSize() { /** * Get the value set in {@link SessionParams#setOriginatingUri(Uri)}. + * Note: This value will only be non-null for the owner of the session. */ public @Nullable Uri getOriginatingUri() { return originatingUri; @@ -2096,6 +2097,7 @@ public int getOriginatingUid() { /** * Get the value set in {@link SessionParams#setReferrerUri(Uri)} + * Note: This value will only be non-null for the owner of the session. */ public @Nullable Uri getReferrerUri() { return referrerUri; diff --git a/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java b/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java index c72707db9560..153337727e96 100644 --- a/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java +++ b/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java @@ -58,6 +58,8 @@ public class BugreportManagerTest { private Handler mHandler; private Executor mExecutor; private BugreportManager mBrm; + private File mBugreportFile; + private File mScreenshotFile; private ParcelFileDescriptor mBugreportFd; private ParcelFileDescriptor mScreenshotFd; @@ -73,8 +75,10 @@ public void setup() throws Exception { }; mBrm = getBugreportManager(); - mBugreportFd = parcelFd("bugreport_" + name.getMethodName(), ".zip"); - mScreenshotFd = parcelFd("screenshot_" + name.getMethodName(), ".png"); + mBugreportFile = createTempFile("bugreport_" + name.getMethodName(), ".zip"); + mScreenshotFile = createTempFile("screenshot_" + name.getMethodName(), ".png"); + mBugreportFd = parcelFd(mBugreportFile); + mScreenshotFd = parcelFd(mScreenshotFile); getPermissions(); } @@ -120,6 +124,21 @@ public void normalFlow_interactive() throws Exception { assertFdsAreClosed(mBugreportFd); } + @Test + public void normalFlow_full() throws Exception { + BugreportCallbackImpl callback = new BugreportCallbackImpl(); + mBrm.startBugreport(mBugreportFd, mScreenshotFd, full(), mExecutor, callback); + + waitTillDoneOrTimeout(callback); + assertThat(callback.isDone()).isTrue(); + assertThat(callback.getErrorCode()).isEqualTo( + BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT); + // bugreport and screenshot files should be empty when user consent timed out. + assertThat(mBugreportFile.length()).isEqualTo(0); + assertThat(mScreenshotFile.length()).isEqualTo(0); + assertFdsAreClosed(mBugreportFd, mScreenshotFd); + } + @Test public void simultaneousBugreportsNotAllowed() throws Exception { // Start bugreport #1 @@ -129,9 +148,10 @@ public void simultaneousBugreportsNotAllowed() throws Exception { // Before #1 is done, try to start #2. assertThat(callback.isDone()).isFalse(); BugreportCallbackImpl callback2 = new BugreportCallbackImpl(); - ParcelFileDescriptor bugreportFd2 = parcelFd("bugreport_2_" + name.getMethodName(), ".zip"); - ParcelFileDescriptor screenshotFd2 = - parcelFd("screenshot_2_" + name.getMethodName(), ".png"); + File bugreportFile2 = createTempFile("bugreport_2_" + name.getMethodName(), ".zip"); + File screenshotFile2 = createTempFile("screenshot_2_" + name.getMethodName(), ".png"); + ParcelFileDescriptor bugreportFd2 = parcelFd(bugreportFile2); + ParcelFileDescriptor screenshotFd2 = parcelFd(screenshotFile2); mBrm.startBugreport(bugreportFd2, screenshotFd2, wifi(), mExecutor, callback2); Thread.sleep(500 /* .5s */); @@ -271,12 +291,16 @@ public static BugreportManager getBugreportManager() { return bm; } - private static ParcelFileDescriptor parcelFd(String prefix, String extension) throws Exception { - File f = File.createTempFile(prefix, extension); + private static File createTempFile(String prefix, String extension) throws Exception { + final File f = File.createTempFile(prefix, extension); f.setReadable(true, true); f.setWritable(true, true); + f.deleteOnExit(); + return f; + } - return ParcelFileDescriptor.open(f, + private static ParcelFileDescriptor parcelFd(File file) throws Exception { + return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_APPEND); } @@ -342,4 +366,13 @@ private static BugreportParams wifi() { private static BugreportParams interactive() { return new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE); } + + /* + * Returns a {@link BugreportParams} for full bugreport that includes a screenshot. + * + *

This can take on the order of minutes to finish + */ + private static BugreportParams full() { + return new BugreportParams(BugreportParams.BUGREPORT_MODE_FULL); + } } diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java index 325eb17342e1..a6f3006f3366 100644 --- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java +++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java @@ -18,7 +18,6 @@ import android.app.Notification; import android.app.NotificationManager; -import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -392,13 +391,9 @@ private synchronized void setNiNotification(GpsNiNotification notif) { mNiNotificationBuilder.setDefaults(0); } - // if not to popup dialog immediately, pending intent will open the dialog - Intent intent = !mPopupImmediately ? getDlgIntent(notif) : new Intent(); - PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); mNiNotificationBuilder.setTicker(getNotifTicker(notif, mContext)) .setContentTitle(title) - .setContentText(message) - .setContentIntent(pi); + .setContentText(message); notificationManager.notifyAsUser(null, notif.notificationId, mNiNotificationBuilder.build(), UserHandle.ALL); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index 06c7940b67c3..44e480adca96 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -547,7 +547,7 @@ private Notification createSaveNotification(Uri uri) { this, REQUEST_CODE, viewIntent, - Intent.FLAG_GRANT_READ_URI_PERMISSION)) + PendingIntent.FLAG_IMMUTABLE)) .addAction(shareAction) .addAction(deleteAction) .setAutoCancel(true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 969e94678055..439d832fe465 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -577,7 +577,7 @@ public NotificationStackScrollLayout( res.getBoolean(R.bool.config_fadeNotificationsOnDismiss); mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated); mRoundnessManager.setOnRoundingChangedCallback(this::invalidate); - addOnExpandedHeightChangedListener(mRoundnessManager::setExpanded); + addOnExpandedHeightListener(mRoundnessManager::setExpanded); mLockscreenUserManager.addUserChangedListener(userId -> updateSensitiveness(false /* animated */)); setOutlineProvider(mOutlineProvider); @@ -585,7 +585,7 @@ public NotificationStackScrollLayout( // Blocking helper manager wants to know the expanded state, update as well. NotificationBlockingHelperManager blockingHelperManager = Dependency.get(NotificationBlockingHelperManager.class); - addOnExpandedHeightChangedListener((height, unused) -> { + addOnExpandedHeightListener((height, unused) -> { blockingHelperManager.setNotificationShadeExpanded(height); }); diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java b/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java index b25df5f9c07f..5e7280840bb9 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java @@ -104,9 +104,13 @@ public void dumpLeak(int garbageCount) { .setContentText(String.format( "SystemUI has detected %d leaked objects. Tap to send", garbageCount)) .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) - .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, + .setContentIntent(PendingIntent.getActivityAsUser( + mContext, + 0, getIntent(hprofFile, dumpFile), - PendingIntent.FLAG_UPDATE_CURRENT, null, UserHandle.CURRENT)); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE, + null, + UserHandle.CURRENT)); notiMan.notify(TAG, 0, builder.build()); } catch (IOException e) { Log.e(TAG, "Couldn't dump heap for leak", e); diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index 5d09d3905e34..0937a568c140 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -33,6 +33,8 @@ import static java.lang.Thread.sleep; +import static java.lang.Thread.sleep; + import android.app.AppOpsManager; import android.content.pm.PackageManager; import android.os.UserHandle; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 7c9537b95319..079eaa2b0fda 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -120,7 +120,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private NotificationIconAreaController mNotificationIconAreaController; @Mock private MetricsLogger mMetricsLogger; @Mock private NotificationRoundnessManager mNotificationRoundnessManager; - @Mock private KeyguardBypassController mKeyguardBypassController; @Mock private NotificationLockscreenUserManager mLockscreenUserManager; private UserChangedListener mUserChangedListener; private TestableNotificationEntryManager mEntryManager; @@ -141,9 +140,9 @@ public void setUp() throws Exception { mDependency.injectTestDependency( NotificationBlockingHelperManager.class, mBlockingHelperManager); - mDependency.injectTestDependency(SysuiStatusBarStateController.class, mBarState); mDependency.injectTestDependency(NotificationLockscreenUserManager.class, mLockscreenUserManager); + mDependency.injectTestDependency(StatusBarStateController.class, mBarState); mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); mDependency.injectTestDependency(NotificationRemoteInputManager.class, mRemoteInputManager); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ff84688654a9..a210749ee99c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -3235,6 +3235,10 @@ public int getPackageProcessState(String packageName, String callingPackage) { @Override public boolean setProcessMemoryTrimLevel(String process, int userId, int level) throws RemoteException { + if (!isCallerShell()) { + EventLog.writeEvent(0x534e4554, 160390416, Binder.getCallingUid(), ""); + throw new SecurityException("Only shell can call it"); + } synchronized (this) { final ProcessRecord app = findProcessLocked(process, userId, "setProcessMemoryTrimLevel"); if (app == null) { diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 687ca1927882..6b8c6c3f23ee 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -1847,22 +1847,15 @@ private int checkOperationImpl(int code, int uid, String packageName, * @return The mode of the op */ private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName, - boolean raw) { + boolean raw, boolean verify) { if (isOpRestrictedDueToSuspend(code, packageName, uid)) { return AppOpsManager.MODE_IGNORED; } - - boolean isPrivileged; - - try { - isPrivileged = verifyAndGetIsPrivileged(uid, packageName); - } catch (SecurityException e) { - Slog.e(TAG, "checkOperation", e); - return AppOpsManager.opToDefaultMode(code); - } - synchronized (this) { - if (isOpRestrictedLocked(uid, code, packageName, isPrivileged)) { + if (verify) { + checkPackage(uid, packageName); + } + if (isOpRestrictedLocked(uid, code, packageName)) { return AppOpsManager.MODE_IGNORED; } code = AppOpsManager.opToSwitch(code); @@ -2777,8 +2770,7 @@ private boolean isOpRestrictedDueToSuspend(int code, String packageName, int uid return pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid)); } - private boolean isOpRestrictedLocked(int uid, int code, String packageName, - boolean isPrivileged) { + private boolean isOpRestrictedLocked(int uid, int code, String packageName) { int userHandle = UserHandle.getUserId(uid); final int restrictionSetCount = mOpUserRestrictions.size(); diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java index dd522b95a938..0f38473ef11a 100644 --- a/services/core/java/com/android/server/location/GnssVisibilityControl.java +++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java @@ -21,7 +21,6 @@ import android.app.AppOpsManager; import android.app.Notification; import android.app.NotificationManager; -import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -645,7 +644,6 @@ private static Notification createEmergencyLocationUserNotification(Context cont .setTicker(accessibilityServicesText) .setContentTitle(firstLineText) .setContentText(secondLineText) - .setContentIntent(PendingIntent.getBroadcast(context, 0, new Intent(), 0)) .build(); } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 8227e58bf7e0..ba8376222b97 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -770,26 +770,30 @@ private String buildExternalStageCid(int sessionId) { public SessionInfo getSessionInfo(int sessionId) { synchronized (mSessions) { final PackageInstallerSession session = mSessions.get(sessionId); - return session != null ? session.generateInfo() : null; + + return session != null + ? session.generateInfoForCaller(true /*withIcon*/, Binder.getCallingUid()) + : null; } } @Override public ParceledListSlice getStagedSessions() { - return mStagingManager.getSessions(); + return mStagingManager.getSessions(Binder.getCallingUid()); } @Override public ParceledListSlice getAllSessions(int userId) { + final int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission( - Binder.getCallingUid(), userId, true, false, "getAllSessions"); + callingUid, userId, true, false, "getAllSessions"); final List result = new ArrayList<>(); synchronized (mSessions) { for (int i = 0; i < mSessions.size(); i++) { final PackageInstallerSession session = mSessions.valueAt(i); if (session.userId == userId && !session.hasParentSessionId()) { - result.add(session.generateInfo(false)); + result.add(session.generateInfoForCaller(false, callingUid)); } } } @@ -807,7 +811,8 @@ public ParceledListSlice getMySessions(String installerPackageName, for (int i = 0; i < mSessions.size(); i++) { final PackageInstallerSession session = mSessions.valueAt(i); - SessionInfo info = session.generateInfo(false); + SessionInfo info = + session.generateInfoForCaller(false /*withIcon*/, Process.SYSTEM_UID); if (Objects.equals(info.getInstallerPackageName(), installerPackageName) && session.userId == userId && !session.hasParentSessionId()) { result.add(info); @@ -1251,7 +1256,10 @@ public void onStagedSessionChanged(PackageInstallerSession session) { session.markUpdated(); writeSessionsAsync(); if (mOkToSendBroadcasts) { - mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), + // we don't scrub the data here as this is sent only to the installer several + // privileged system packages + mPm.sendSessionUpdatedBroadcast( + session.generateInfoForCaller(false/*icon*/, Process.SYSTEM_UID), session.userId); } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index b661a8553698..a421f8f27b91 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -457,11 +457,41 @@ public PackageInstallerSession(PackageInstallerService.InternalCallback callback stagedSessionErrorMessage != null ? stagedSessionErrorMessage : ""; } - public SessionInfo generateInfo() { - return generateInfo(true); + /** + * Returns {@code true} if the {@link SessionInfo} object should be produced with potentially + * sensitive data scrubbed from its fields. + * + * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may + * need to be scrubbed + */ + private boolean shouldScrubData(int callingUid) { + return !(callingUid < Process.FIRST_APPLICATION_UID || getInstallerUid() == callingUid); + } + + /** + * Generates a {@link SessionInfo} object for the provided uid. This may result in some fields + * that may contain sensitive info being filtered. + * + * @param includeIcon true if the icon should be included in the object + * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may + * need to be scrubbed + * @see #shouldScrubData(int) + */ + public SessionInfo generateInfoForCaller(boolean includeIcon, int callingUid) { + return generateInfoInternal(includeIcon, shouldScrubData(callingUid)); } - public SessionInfo generateInfo(boolean includeIcon) { + /** + * Generates a {@link SessionInfo} object to ensure proper hiding of sensitive fields. + * + * @param includeIcon true if the icon should be included in the object + * @see #generateInfoForCaller(boolean, int) + */ + public SessionInfo generateInfoScrubbed(boolean includeIcon) { + return generateInfoInternal(includeIcon, true /*scrubData*/); + } + + private SessionInfo generateInfoInternal(boolean includeIcon, boolean scrubData) { final SessionInfo info = new SessionInfo(); synchronized (mLock) { info.sessionId = sessionId; @@ -484,9 +514,13 @@ public SessionInfo generateInfo(boolean includeIcon) { info.appLabel = params.appLabel; info.installLocation = params.installLocation; - info.originatingUri = params.originatingUri; + if (!scrubData) { + info.originatingUri = params.originatingUri; + } info.originatingUid = params.originatingUid; - info.referrerUri = params.referrerUri; + if (!scrubData) { + info.referrerUri = params.referrerUri; + } info.grantedRuntimePermissions = params.grantedRuntimePermissions; info.whitelistedRestrictedPermissions = params.whitelistedRestrictedPermissions; info.installFlags = params.installFlags; @@ -2188,9 +2222,8 @@ private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) // Send broadcast to default launcher only if it's a new install // TODO(b/144270665): Secure the usage of this broadcast. final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING); - if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts() - && (params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { - mPm.sendSessionCommitBroadcast(generateInfo(), userId); + if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) { + mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId); } mCallback.onSessionFinished(this, success); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 422b355f9424..ff8381f6fb67 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1143,13 +1143,6 @@ public void receiveVerificationResponse(int verificationId) { int updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; boolean needUpdate = false; - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.d(TAG, - "Updating IntentFilterVerificationInfo for package " + packageName - + " verificationId:" + verificationId - + " verified=" + verified); - } - // In a success case, we promote from undefined or ASK to ALWAYS. This // supports a flow where the app fails validation but then ships an updated // APK that passes, and therefore deserves to be in ALWAYS. @@ -11783,6 +11776,8 @@ private static void applyPolicy(PackageParser.Package pkg, final @ParseFlags int ~ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE; pkg.applicationInfo.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE; + // clear protected broadcasts + pkg.protectedBroadcasts = null; // cap permission priorities if (pkg.permissionGroups != null && pkg.permissionGroups.size() > 0) { for (int i = pkg.permissionGroups.size() - 1; i >= 0; --i) { @@ -11791,8 +11786,6 @@ private static void applyPolicy(PackageParser.Package pkg, final @ParseFlags int } } if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) { - // clear protected broadcasts - pkg.protectedBroadcasts = null; // ignore export request for single user receivers if (pkg.receivers != null) { for (int i = pkg.receivers.size() - 1; i >= 0; --i) { @@ -14698,19 +14691,32 @@ public void setInstallerPackageName(String targetPackage, String installerPackag // Verify: if target already has an installer package, it must // be signed with the same cert as the caller. - if (targetPackageSetting.installerPackageName != null) { - PackageSetting setting = mSettings.mPackages.get( - targetPackageSetting.installerPackageName); - // If the currently set package isn't valid, then it's always - // okay to change it. - if (setting != null) { - if (compareSignatures(callerSignature, - setting.signatures.mSigningDetails.signatures) - != PackageManager.SIGNATURE_MATCH) { - throw new SecurityException( - "Caller does not have same cert as old installer package " - + targetPackageSetting.installerPackageName); - } + String targetInstallerPackageName = + targetPackageSetting.installerPackageName; + PackageSetting targetInstallerPkgSetting = targetInstallerPackageName == null ? null : + mSettings.mPackages.get(targetInstallerPackageName); + + if (targetInstallerPkgSetting != null) { + if (compareSignatures(callerSignature, + targetInstallerPkgSetting.signatures.mSigningDetails.signatures) + != PackageManager.SIGNATURE_MATCH) { + throw new SecurityException( + "Caller does not have same cert as old installer package " + + targetInstallerPackageName); + } + } else if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) + != PackageManager.PERMISSION_GRANTED) { + // This is probably an attempt to exploit vulnerability b/150857253 of taking + // privileged installer permissions when the installer has been uninstalled or + // was never set. + EventLog.writeEvent(0x534e4554, "150857253", callingUid, ""); + + if (getUidTargetSdkVersionLockedLPr(callingUid) > Build.VERSION_CODES.Q) { + throw new SecurityException("Neither user " + callingUid + + " nor current process has " + Manifest.permission.INSTALL_PACKAGES); + } else { + // If not targeting >Q, fail silently for backwards compatibility + return; } } @@ -18280,16 +18286,18 @@ private void verifyIntentFiltersIfNeeded(int userId, int verifierUid, boolean re int count = 0; final String packageName = pkg.packageName; - boolean handlesWebUris = false; - final boolean alreadyVerified; + ArraySet domains = new ArraySet<>(); + final boolean previouslyVerified; + boolean hostSetExpanded = false; + boolean needToRunVerify = false; synchronized (mPackages) { // If this is a new install and we see that we've already run verification for this // package, we have nothing to do: it means the state was restored from backup. - final IntentFilterVerificationInfo ivi = + IntentFilterVerificationInfo ivi = mSettings.getIntentFilterVerificationLPr(packageName); - alreadyVerified = (ivi != null); - if (!replacing && alreadyVerified) { + previouslyVerified = (ivi != null); + if (!replacing && previouslyVerified) { if (DEBUG_DOMAIN_VERIFICATION) { Slog.i(TAG, "Package " + packageName + " already verified: status=" + ivi.getStatusString()); @@ -18297,52 +18305,94 @@ private void verifyIntentFiltersIfNeeded(int userId, int verifierUid, boolean re return; } + if (DEBUG_DOMAIN_VERIFICATION) { + Slog.i(TAG, " Previous verified hosts: " + + (ivi == null ? "[none]" : ivi.getDomainsString())); + } + // If any filters need to be verified, then all need to be. In addition, we need to // know whether an updating app has any web navigation intent filters, to re- // examine handling policy even if not re-verifying. - boolean needToVerify = false; + final boolean needsVerification = needsNetworkVerificationLPr(packageName); for (PackageParser.Activity a : pkg.activities) { for (ActivityIntentInfo filter : a.intents) { if (filter.handlesWebUris(true)) { handlesWebUris = true; } - if (filter.needsVerification() && needsNetworkVerificationLPr(filter)) { + if (needsVerification && filter.needsVerification()) { if (DEBUG_DOMAIN_VERIFICATION) { - Slog.d(TAG, - "Intent filter needs verification, so processing all filters"); + Slog.d(TAG, "autoVerify requested, processing all filters"); } - needToVerify = true; + needToRunVerify = true; // It's safe to break out here because filter.needsVerification() - // can only be true if filter.handlesWebUris(true) returns true, so + // can only be true if filter.handlesWebUris(true) returned true, so // we've already noted that. break; } } } - // Note whether this app publishes any web navigation handling support at all, - // and whether there are any web-nav filters that fit the profile for running - // a verification pass now. - if (needToVerify) { + // Compare the new set of recognized hosts if the app is either requesting + // autoVerify or has previously used autoVerify but no longer does. + if (needToRunVerify || previouslyVerified) { final int verificationId = mIntentFilterVerificationToken++; for (PackageParser.Activity a : pkg.activities) { for (ActivityIntentInfo filter : a.intents) { // Run verification against hosts mentioned in any web-nav intent filter, // even if the filter matches non-web schemes as well - if (filter.handlesWebUris(false) && needsNetworkVerificationLPr(filter)) { + if (filter.handlesWebUris(false /*onlyWebSchemes*/)) { if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Verification needed for IntentFilter:" + filter.toString()); mIntentFilterVerifier.addOneIntentFilterVerification( verifierUid, userId, verificationId, filter, packageName); + domains.addAll(filter.getHostsList()); count++; } } } } + + if (DEBUG_DOMAIN_VERIFICATION) { + Slog.i(TAG, " Update published hosts: " + domains.toString()); + } + + // If we've previously verified this same host set (or a subset), we can trust that + // a current ALWAYS policy is still applicable. If this is the case, we're done. + // (If we aren't in ALWAYS, we want to reverify to allow for apps that had failing + // hosts in their intent filters, then pushed a new apk that removed them and now + // passes.) + // + // Cases: + // + still autoVerify (needToRunVerify): + // - preserve current state if all of: unexpanded, in always + // - otherwise rerun as usual (fall through) + // + no longer autoVerify (alreadyVerified && !needToRunVerify) + // - wipe verification history always + // - preserve current state if all of: unexpanded, in always + hostSetExpanded = !previouslyVerified + || (ivi != null && !ivi.getDomains().containsAll(domains)); + final int currentPolicy = + mSettings.getIntentFilterVerificationStatusLPr(packageName, userId); + final boolean keepCurState = !hostSetExpanded + && currentPolicy == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; + + if (needToRunVerify && keepCurState) { + if (DEBUG_DOMAIN_VERIFICATION) { + Slog.i(TAG, "Host set not expanding + ALWAYS -> no need to reverify"); + } + ivi.setDomains(domains); + scheduleWriteSettingsLocked(); + return; + } else if (previouslyVerified && !needToRunVerify) { + // Prior autoVerify state but not requesting it now. Clear autoVerify history, + // and preserve the always policy iff the host set is not expanding. + clearIntentFilterVerificationsLPw(packageName, userId, !keepCurState); + return; + } } - if (count > 0) { - // count > 0 means that we're running a full verification pass + if (needToRunVerify && count > 0) { + // app requested autoVerify and has at least one matching intent filter if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Starting " + count + " IntentFilter verification" + (count > 1 ? "s" : "") + " for userId:" + userId); @@ -18358,16 +18408,13 @@ private void verifyIntentFiltersIfNeeded(int userId, int verifierUid, boolean re } } else { if (DEBUG_DOMAIN_VERIFICATION) { - Slog.d(TAG, "No web filters or no prior verify policy for " + packageName); + Slog.d(TAG, "No web filters or no new host policy for " + packageName); } } } @GuardedBy("mPackages") - private boolean needsNetworkVerificationLPr(ActivityIntentInfo filter) { - final ComponentName cn = filter.activity.getComponentName(); - final String packageName = cn.getPackageName(); - + private boolean needsNetworkVerificationLPr(String packageName) { IntentFilterVerificationInfo ivi = mSettings.getIntentFilterVerificationLPr( packageName); if (ivi == null) { @@ -19113,7 +19160,7 @@ private void removePackageDataLIF(final PackageSetting deletedPs, int[] allUserH if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { final SparseBooleanArray changedUsers = new SparseBooleanArray(); synchronized (mPackages) { - clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL); + clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL, true); clearDefaultBrowserIfNeeded(packageName); mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName); removedAppId = mSettings.removePackageLPw(packageName); @@ -20618,13 +20665,14 @@ private void clearIntentFilterVerificationsLPw(int userId) { final int packageCount = mPackages.size(); for (int i = 0; i < packageCount; i++) { PackageParser.Package pkg = mPackages.valueAt(i); - clearIntentFilterVerificationsLPw(pkg.packageName, userId); + clearIntentFilterVerificationsLPw(pkg.packageName, userId, true); } } /** This method takes a specific user id as well as UserHandle.USER_ALL. */ @GuardedBy("mPackages") - void clearIntentFilterVerificationsLPw(String packageName, int userId) { + void clearIntentFilterVerificationsLPw(String packageName, int userId, + boolean alsoResetStatus) { if (userId == UserHandle.USER_ALL) { if (mSettings.removeIntentFilterVerificationLPw(packageName, sUserManager.getUserIds())) { @@ -20633,7 +20681,8 @@ void clearIntentFilterVerificationsLPw(String packageName, int userId) { } } } else { - if (mSettings.removeIntentFilterVerificationLPw(packageName, userId)) { + if (mSettings.removeIntentFilterVerificationLPw(packageName, userId, + alsoResetStatus)) { scheduleWritePackageRestrictionsLocked(userId); } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 062e28a73a18..a3dbfdc099d2 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -1243,7 +1243,8 @@ List getIntentFilterVerificationsLPr( return result; } - boolean removeIntentFilterVerificationLPw(String packageName, int userId) { + boolean removeIntentFilterVerificationLPw(String packageName, int userId, + boolean alsoResetStatus) { PackageSetting ps = mPackages.get(packageName); if (ps == null) { if (DEBUG_DOMAIN_VERIFICATION) { @@ -1251,7 +1252,9 @@ boolean removeIntentFilterVerificationLPw(String packageName, int userId) { } return false; } - ps.clearDomainVerificationStatusForUser(userId); + if (alsoResetStatus) { + ps.clearDomainVerificationStatusForUser(userId); + } ps.setIntentFilterVerificationInfo(null); return true; } @@ -1259,7 +1262,7 @@ boolean removeIntentFilterVerificationLPw(String packageName, int userId) { boolean removeIntentFilterVerificationLPw(String packageName, int[] userIds) { boolean result = false; for (int userId : userIds) { - result |= removeIntentFilterVerificationLPw(packageName, userId); + result |= removeIntentFilterVerificationLPw(packageName, userId, true); } return result; } diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 895d2c5d00bf..dd7133121b21 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -95,11 +95,12 @@ private void updateStoredSession(@NonNull PackageInstallerSession sessionInfo) { } } - ParceledListSlice getSessions() { + ParceledListSlice getSessions(int callingUid) { final List result = new ArrayList<>(); synchronized (mStagedSessions) { for (int i = 0; i < mStagedSessions.size(); i++) { - result.add(mStagedSessions.valueAt(i).generateInfo(false)); + final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i); + result.add(stagedSession.generateInfoForCaller(false /*icon*/, callingUid)); } } return new ParceledListSlice<>(result); diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 0e2f0ce991c7..0ec366c91528 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -715,6 +715,7 @@ private void generateCrop(WallpaperData wallpaper) { estimateCrop.bottom = estimateCrop.top + newHeight; cropHint.set(estimateCrop); estimateCrop.scale(1f / options.inSampleSize); + } // We've got the safe cropHint; now we want to scale it properly to From 6d7d8f77186fa5345aa6ee1374aafbe3a185b11a Mon Sep 17 00:00:00 2001 From: RakeshBatra Date: Wed, 9 Sep 2020 17:12:02 +0000 Subject: [PATCH 2/3] Bunch of fixes after r46 merge --- .../stack/NotificationStackScrollLayout.java | 2 +- .../phone/HeadsUpAppearanceController.java | 2 +- .../android/server/appop/AppOpsService.java | 298 +++++++----------- .../server/pm/PackageManagerService.java | 9 - 4 files changed, 123 insertions(+), 188 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 439d832fe465..ab639a0e9db0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -5470,7 +5470,7 @@ public boolean isFullyHidden() { * @param listener the listener to notify. */ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void addOnExpandedHeightChangedListener(BiConsumer listener) { + public void addOnExpandedHeightListener(BiConsumer listener) { mExpandedHeightListeners.add(listener); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 08d34f1b27a5..59eafa409fd5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -139,7 +139,7 @@ public HeadsUpAppearanceController( panelView.addTrackingHeadsUpListener(mSetTrackingHeadsUp); panelView.addVerticalTranslationListener(mUpdatePanelTranslation); panelView.setHeadsUpAppearanceController(this); - mStackScroller.addOnExpandedHeightChangedListener(mSetExpandedHeight); + mStackScroller.addOnExpandedHeightListener(mSetExpandedHeight); mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener); mStackScroller.setHeadsUpAppearanceController(this); mClockView = clockView; diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 6b8c6c3f23ee..01f6b22ebc67 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -521,10 +521,6 @@ private void updateAccessTimeAndDuration(long time, long duration, private void updateProxyState(long key, int proxyUid, @Nullable String proxyPackageName) { - if (proxyUid == Process.INVALID_UID) { - return; - } - if (mProxyUids == null) { mProxyUids = new LongSparseLongArray(); } @@ -720,12 +716,7 @@ public String toString() { public void binderDied() { synchronized (AppOpsService.this) { for (int i=mStartedOps.size()-1; i>=0; i--) { - final Op op = mStartedOps.get(i); - finishOperationLocked(op, /*finishNested*/ true); - if (op.startNesting <= 0) { - scheduleOpActiveChangedIfNeededLocked(op.op, op.uidState.uid, - op.packageName, false); - } + finishOperationLocked(mStartedOps.get(i), /*finishNested*/ true); } mClients.remove(mAppToken); } @@ -1131,8 +1122,8 @@ public List getOpsForPackage(int uid, String packageNa return Collections.emptyList(); } synchronized (this) { - Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false /* isPrivileged */, - false /* edit */); + Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false /* edit */, + false /* uidMismatchExpected */); if (pkgOps == null) { return null; } @@ -1229,7 +1220,8 @@ public List getUidOps(int uid, int[] ops) { private void pruneOp(Op op, int uid, String packageName) { if (!op.hasAnyTime()) { - Ops ops = getOpsRawLocked(uid, packageName, false /* isPrivileged */, false /* edit */); + Ops ops = getOpsRawLocked(uid, packageName, false /* edit */, + false /* uidMismatchExpected */); if (ops != null) { ops.remove(op.op); if (ops.size() <= 0) { @@ -1446,6 +1438,11 @@ private void setAllPkgModesToDefault(int code, int uid) { } } + @Override + public void setMode(int code, int uid, String packageName, int mode) { + setMode(code, uid, packageName, mode, true, false); + } + /** * Sets the mode for a certain op and uid. * @@ -1453,25 +1450,19 @@ private void setAllPkgModesToDefault(int code, int uid) { * @param uid The UID for which to set * @param packageName The package for which to set * @param mode The new mode to set + * @param verifyUid Iff {@code true}, check that the package name belongs to the uid + * @param isPrivileged Whether the package is privileged. (Only used if {@code verifyUid == + * false}) */ - @Override - public void setMode(int code, int uid, @NonNull String packageName, int mode) { + private void setMode(int code, int uid, @NonNull String packageName, int mode, + boolean verifyUid, boolean isPrivileged) { enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid); verifyIncomingOp(code); ArraySet repCbs = null; code = AppOpsManager.opToSwitch(code); - - boolean isPrivileged; - try { - isPrivileged = verifyAndGetIsPrivileged(uid, packageName); - } catch (SecurityException e) { - Slog.e(TAG, "Cannot setMode", e); - return; - } - synchronized (this) { UidState uidState = getUidStateLocked(uid, false); - Op op = getOpLocked(code, uid, packageName, isPrivileged, true); + Op op = getOpLocked(code, uid, packageName, true, verifyUid, isPrivileged); if (op != null) { if (op.mode != mode) { op.mode = mode; @@ -1836,6 +1827,14 @@ private int checkOperationImpl(int code, int uid, String packageName, return checkOperationUnchecked(code, uid, resolvedPackageName, raw); } + /** + * @see #checkOperationUnchecked(int, int, String, boolean, boolean) + */ + private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName, + boolean raw) { + return checkOperationUnchecked(code, uid, packageName, raw, true); + } + /** * Get the mode of an app-op. * @@ -1843,6 +1842,7 @@ private int checkOperationImpl(int code, int uid, String packageName, * @param uid The uid of the package the op belongs to * @param packageName The package the op belongs to * @param raw If the raw state of eval-ed state should be checked. + * @param verify If the code should check the package belongs to the uid * * @return The mode of the op */ @@ -1852,7 +1852,7 @@ private int checkOperationImpl(int code, int uid, String packageName, return AppOpsManager.MODE_IGNORED; } synchronized (this) { - if (verify) { + if (verify) { checkPackage(uid, packageName); } if (isOpRestrictedLocked(uid, code, packageName)) { @@ -1865,7 +1865,7 @@ private int checkOperationImpl(int code, int uid, String packageName, final int rawMode = uidState.opModes.get(code); return raw ? rawMode : uidState.evalMode(code, rawMode); } - Op op = getOpLocked(code, uid, packageName, false, false); + Op op = getOpLocked(code, uid, packageName, false, verify, false); if (op == null) { return AppOpsManager.opToDefaultMode(code); } @@ -1970,12 +1970,14 @@ public void setAudioRestriction(int code, int usage, int uid, int mode, @Override public int checkPackage(int uid, String packageName) { Preconditions.checkNotNull(packageName); - try { - verifyAndGetIsPrivileged(uid, packageName); - - return AppOpsManager.MODE_ALLOWED; - } catch (SecurityException ignored) { - return AppOpsManager.MODE_ERRORED; + synchronized (this) { + Ops ops = getOpsRawLocked(uid, packageName, true /* edit */, + true /* uidMismatchExpected */); + if (ops != null) { + return AppOpsManager.MODE_ALLOWED; + } else { + return AppOpsManager.MODE_ERRORED; + } } } @@ -2038,16 +2040,9 @@ private int noteOperationImpl(int code, int uid, String packageName) { private int noteOperationUnchecked(int code, int uid, String packageName, int proxyUid, String proxyPackageName, @OpFlags int flags) { - boolean isPrivileged; - try { - isPrivileged = verifyAndGetIsPrivileged(uid, packageName); - } catch (SecurityException e) { - Slog.e(TAG, "noteOperation", e); - return AppOpsManager.MODE_ERRORED; - } - synchronized (this) { - final Ops ops = getOpsRawLocked(uid, packageName, isPrivileged, true /* edit */); + final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */, + false /* uidMismatchExpected */); if (ops == null) { scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED); @@ -2056,7 +2051,7 @@ private int noteOperationUnchecked(int code, int uid, String packageName, return AppOpsManager.MODE_ERRORED; } final Op op = getOpLocked(ops, code, true); - if (isOpRestrictedLocked(uid, code, packageName, isPrivileged)) { + if (isOpRestrictedLocked(uid, code, packageName)) { scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED); return AppOpsManager.MODE_IGNORED; @@ -2215,25 +2210,16 @@ public int startOperation(IBinder token, int code, int uid, String packageName, return AppOpsManager.MODE_IGNORED; } ClientState client = (ClientState)token; - - boolean isPrivileged; - try { - isPrivileged = verifyAndGetIsPrivileged(uid, packageName); - } catch (SecurityException e) { - Slog.e(TAG, "startOperation", e); - return AppOpsManager.MODE_ERRORED; - } - synchronized (this) { - final Ops ops = getOpsRawLocked(uid, resolvedPackageName, isPrivileged, - true /* edit */); + final Ops ops = getOpsRawLocked(uid, resolvedPackageName, true /* edit */, + false /* uidMismatchExpected */); if (ops == null) { if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid + " package " + resolvedPackageName); return AppOpsManager.MODE_ERRORED; } final Op op = getOpLocked(ops, code, true); - if (isOpRestrictedLocked(uid, code, resolvedPackageName, isPrivileged)) { + if (isOpRestrictedLocked(uid, code, resolvedPackageName)) { return AppOpsManager.MODE_IGNORED; } final int switchCode = AppOpsManager.opToSwitch(code); @@ -2305,17 +2291,8 @@ public void finishOperation(IBinder token, int code, int uid, String packageName return; } ClientState client = (ClientState) token; - - boolean isPrivileged; - try { - isPrivileged = verifyAndGetIsPrivileged(uid, packageName); - } catch (SecurityException e) { - Slog.e(TAG, "Cannot finishOperation", e); - return; - } - synchronized (this) { - Op op = getOpLocked(code, uid, resolvedPackageName, isPrivileged, true); + Op op = getOpLocked(code, uid, resolvedPackageName, true, true, false); if (op == null) { return; } @@ -2580,76 +2557,8 @@ private void commitUidPendingStateLocked(UidState uidState) { uidState.pendingStateCommitTime = 0; } - /** - * Verify that package belongs to uid and return whether the package is privileged. - * - * @param uid The uid the package belongs to - * @param packageName The package the might belong to the uid - * - * @return {@code true} iff the package is privileged - */ - private boolean verifyAndGetIsPrivileged(int uid, String packageName) { - if (uid == Process.ROOT_UID) { - // For backwards compatibility, don't check package name for root UID. - return false; - } - - // Do not check if uid/packageName is already known - synchronized (this) { - UidState uidState = mUidStates.get(uid); - if (uidState != null && uidState.pkgOps != null) { - Ops ops = uidState.pkgOps.get(packageName); - - if (ops != null) { - return ops.isPrivileged; - } - } - } - - boolean isPrivileged = false; - final long ident = Binder.clearCallingIdentity(); - try { - int pkgUid; - - ApplicationInfo appInfo = LocalServices.getService(PackageManagerInternal.class) - .getApplicationInfo(packageName, PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS - | PackageManager.MATCH_UNINSTALLED_PACKAGES - | PackageManager.MATCH_INSTANT, - Process.SYSTEM_UID, UserHandle.getUserId(uid)); - if (appInfo != null) { - pkgUid = appInfo.uid; - isPrivileged = (appInfo.privateFlags - & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; - } else { - pkgUid = resolveUid(packageName); - if (pkgUid >= 0) { - isPrivileged = false; - } - } - if (pkgUid != uid) { - throw new SecurityException("Specified package " + packageName + " under uid " + uid - + " but it is really " + pkgUid); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - - return isPrivileged; - } - - /** - * Get (and potentially create) ops. - * - * @param uid The uid the package belongs to - * @param packageName The name of the package - * @param isPrivileged If the package is privilidged (ignored if {@code edit} is false) - * @param edit If an ops does not exist, create the ops? - - * @return - */ - private Ops getOpsRawLocked(int uid, String packageName, boolean isPrivileged, boolean edit) { + private Ops getOpsRawLocked(int uid, String packageName, boolean edit, + boolean uidMismatchExpected) { UidState uidState = getUidStateLocked(uid, edit); if (uidState == null) { return null; @@ -2667,6 +2576,47 @@ private Ops getOpsRawLocked(int uid, String packageName, boolean isPrivileged, b if (!edit) { return null; } + boolean isPrivileged = false; + // This is the first time we have seen this package name under this uid, + // so let's make sure it is valid. + if (uid != 0) { + final long ident = Binder.clearCallingIdentity(); + try { + int pkgUid = -1; + try { + ApplicationInfo appInfo = ActivityThread.getPackageManager() + .getApplicationInfo(packageName, + PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, + UserHandle.getUserId(uid)); + if (appInfo != null) { + pkgUid = appInfo.uid; + isPrivileged = (appInfo.privateFlags + & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; + } else { + pkgUid = resolveUid(packageName); + if (pkgUid >= 0) { + isPrivileged = false; + } + } + } catch (RemoteException e) { + Slog.w(TAG, "Could not contact PackageManager", e); + } + if (pkgUid != uid) { + // Oops! The package name is not valid for the uid they are calling + // under. Abort. + if (!uidMismatchExpected) { + RuntimeException ex = new RuntimeException("here"); + ex.fillInStackTrace(); + Slog.w(TAG, "Bad call: specified package " + packageName + + " under uid " + uid + " but it is really " + pkgUid, ex); + } + return null; + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } ops = new Ops(packageName, uidState, isPrivileged); uidState.pkgOps.put(packageName, ops); } @@ -2674,7 +2624,7 @@ private Ops getOpsRawLocked(int uid, String packageName, boolean isPrivileged, b } /** - * Get the state of all ops for a package. + * Get the state of all ops for a package, don't verify that package belongs to uid. * *

Usually callers should use {@link #getOpLocked} and not call this directly. * @@ -2732,15 +2682,23 @@ private void scheduleFastWriteLocked() { * @param code The code of the op * @param uid The uid the of the package * @param packageName The package name for which to get the state for - * @param isPrivileged Whether the package is privileged or not (only used if {@code edit - * == true}) * @param edit Iff {@code true} create the {@link Op} object if not yet created + * @param verifyUid Iff {@code true} check that the package belongs to the uid + * @param isPrivileged Whether the package is privileged or not (only used if {@code verifyUid + * == false}) * * @return The {@link Op state} of the op */ - private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName, - boolean isPrivileged, boolean edit) { - Ops ops = getOpsRawNoVerifyLocked(uid, packageName, edit, isPrivileged); + private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName, boolean edit, + boolean verifyUid, boolean isPrivileged) { + Ops ops; + + if (verifyUid) { + ops = getOpsRawLocked(uid, packageName, edit, false /* uidMismatchExpected */); + } else { + ops = getOpsRawNoVerifyLocked(uid, packageName, edit, isPrivileged); + } + if (ops == null) { return null; } @@ -2782,8 +2740,8 @@ private boolean isOpRestrictedLocked(int uid, int code, String packageName) { if (AppOpsManager.opAllowSystemBypassRestriction(code)) { // If we are the system, bypass user restrictions for certain codes synchronized (this) { - Ops ops = getOpsRawLocked(uid, packageName, isPrivileged, - true /* edit */); + Ops ops = getOpsRawLocked(uid, packageName, true /* edit */, + false /* uidMismatchExpected */); if ((ops != null) && ops.isPrivileged) { return false; } @@ -3095,40 +3053,17 @@ void writeState() { out.startTag(null, "app-ops"); out.attribute(null, "v", String.valueOf(CURRENT_VERSION)); - SparseArray uidStatesClone; - synchronized (this) { - uidStatesClone = new SparseArray<>(mUidStates.size()); - - final int uidStateCount = mUidStates.size(); - for (int uidStateNum = 0; uidStateNum < uidStateCount; uidStateNum++) { - UidState uidState = mUidStates.valueAt(uidStateNum); - int uid = mUidStates.keyAt(uidStateNum); - - SparseIntArray opModes = uidState.opModes; - if (opModes != null && opModes.size() > 0) { - uidStatesClone.put(uid, new SparseIntArray(opModes.size())); - - final int opCount = opModes.size(); - for (int opCountNum = 0; opCountNum < opCount; opCountNum++) { - uidStatesClone.get(uid).put( - opModes.keyAt(opCountNum), - opModes.valueAt(opCountNum)); - } - } - } - } - - final int uidStateCount = uidStatesClone.size(); - for (int uidStateNum = 0; uidStateNum < uidStateCount; uidStateNum++) { - SparseIntArray opModes = uidStatesClone.valueAt(uidStateNum); - if (opModes != null && opModes.size() > 0) { + final int uidStateCount = mUidStates.size(); + for (int i = 0; i < uidStateCount; i++) { + UidState uidState = mUidStates.valueAt(i); + if (uidState.opModes != null && uidState.opModes.size() > 0) { out.startTag(null, "uid"); - out.attribute(null, "n", - Integer.toString(uidStatesClone.keyAt(uidStateNum))); - final int opCount = opModes.size(); - for (int opCountNum = 0; opCountNum < opCount; opCountNum++) { - final int op = opModes.keyAt(opCountNum); - final int mode = opModes.valueAt(opCountNum); + out.attribute(null, "n", Integer.toString(uidState.uid)); + SparseIntArray uidOpModes = uidState.opModes; + final int opCount = uidOpModes.size(); + for (int j = 0; j < opCount; j++) { + final int op = uidOpModes.keyAt(j); + final int mode = uidOpModes.valueAt(j); out.startTag(null, "op"); out.attribute(null, "n", Integer.toString(op)); out.attribute(null, "m", Integer.toString(mode)); @@ -3154,7 +3089,7 @@ void writeState() { out.attribute(null, "n", Integer.toString(pkg.getUid())); synchronized (this) { Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(), - false /* isPrivileged */, false /* edit */); + false /* edit */, false /* uidMismatchExpected */); // Should always be present as the list of PackageOps is generated // from Ops. if (ops != null) { @@ -4732,9 +4667,18 @@ private final class AppOpsManagerInternalImpl extends AppOpsManagerInternal { } } + public void setUidMode(int code, int uid, int mode) { + AppOpsService.this.setUidMode(code, uid, mode); + } + @Override public void setAllPkgModesToDefault(int code, int uid) { AppOpsService.this.setAllPkgModesToDefault(code, uid); } + + public @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName) { + return AppOpsService.this.checkOperationUnchecked(code, uid, packageName, true, false); + } } } + diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ff8381f6fb67..52e642784dde 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -18397,15 +18397,6 @@ private void verifyIntentFiltersIfNeeded(int userId, int verifierUid, boolean re + " IntentFilter verification" + (count > 1 ? "s" : "") + " for userId:" + userId); mIntentFilterVerifier.startVerifications(userId); - } else if (alreadyVerified && handlesWebUris) { - // App used autoVerify in the past, no longer does, but still handles web - // navigation starts. - if (DEBUG_DOMAIN_VERIFICATION) { - Slog.d(TAG, "App changed web filters but no longer verifying - resetting policy"); - } - synchronized (mPackages) { - clearIntentFilterVerificationsLPw(packageName, userId); - } } else { if (DEBUG_DOMAIN_VERIFICATION) { Slog.d(TAG, "No web filters or no new host policy for " + packageName); From ad35a219e570ec6492710d400f8ea8ee64ad34ab Mon Sep 17 00:00:00 2001 From: Chris Crump Date: Mon, 30 Sep 2019 16:47:42 -0400 Subject: [PATCH 3/3] SystemUI: Introduce user interface for Alert Sliders Ported from OxygenOS and reworked for our alert slider implementation. We target AudioManager instead of Zen, icons are also the same as aosp and the dialog uses the material theme as well as support for our themes. To use, the alert slider config must be enabled. By default, the dialog shows on the left side. To move it to the right side, set the location config to 1. Change-Id: Ie1954a44cc5242c95a731abd7404379ea638fe70 --- core/res/res/values/colt_config.xml | 8 + core/res/res/values/colt_symbols.xml | 4 + .../res/drawable/dialog_tri_state_down_bg.xml | 29 ++ .../drawable/dialog_tri_state_middle_bg.xml | 29 ++ .../dialog_tri_state_navigation_bg.xml | 29 ++ .../dialog_tri_state_triangle_right.xml | 27 + .../dialog_tri_state_triangle_top.xml | 27 + .../res/drawable/dialog_tri_state_up_bg.xml | 29 ++ .../SystemUI/res/layout/tri_state_dialog.xml | 62 +++ packages/SystemUI/res/values/colt_dimens.xml | 32 ++ packages/SystemUI/res/values/colt_styles.xml | 6 + .../tristate/TriStateUiController.java | 32 ++ .../tristate/TriStateUiControllerImpl.java | 476 ++++++++++++++++++ .../volume/VolumeDialogComponent.java | 19 +- 14 files changed, 808 insertions(+), 1 deletion(-) create mode 100644 packages/SystemUI/res/drawable/dialog_tri_state_down_bg.xml create mode 100644 packages/SystemUI/res/drawable/dialog_tri_state_middle_bg.xml create mode 100644 packages/SystemUI/res/drawable/dialog_tri_state_navigation_bg.xml create mode 100644 packages/SystemUI/res/drawable/dialog_tri_state_triangle_right.xml create mode 100644 packages/SystemUI/res/drawable/dialog_tri_state_triangle_top.xml create mode 100644 packages/SystemUI/res/drawable/dialog_tri_state_up_bg.xml create mode 100644 packages/SystemUI/res/layout/tri_state_dialog.xml create mode 100644 packages/SystemUI/src/com/android/systemui/tristate/TriStateUiController.java create mode 100644 packages/SystemUI/src/com/android/systemui/tristate/TriStateUiControllerImpl.java diff --git a/core/res/res/values/colt_config.xml b/core/res/res/values/colt_config.xml index 84d85900730a..9e29c10ea139 100644 --- a/core/res/res/values/colt_config.xml +++ b/core/res/res/values/colt_config.xml @@ -183,4 +183,12 @@ 1 1 + + false + + + 0 + diff --git a/core/res/res/values/colt_symbols.xml b/core/res/res/values/colt_symbols.xml index a6f86e33d721..f7b0cc543d0d 100644 --- a/core/res/res/values/colt_symbols.xml +++ b/core/res/res/values/colt_symbols.xml @@ -237,6 +237,10 @@ + + + + diff --git a/packages/SystemUI/res/drawable/dialog_tri_state_down_bg.xml b/packages/SystemUI/res/drawable/dialog_tri_state_down_bg.xml new file mode 100644 index 000000000000..7dddc9d0e930 --- /dev/null +++ b/packages/SystemUI/res/drawable/dialog_tri_state_down_bg.xml @@ -0,0 +1,29 @@ + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/dialog_tri_state_middle_bg.xml b/packages/SystemUI/res/drawable/dialog_tri_state_middle_bg.xml new file mode 100644 index 000000000000..7cde6be2808c --- /dev/null +++ b/packages/SystemUI/res/drawable/dialog_tri_state_middle_bg.xml @@ -0,0 +1,29 @@ + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/dialog_tri_state_navigation_bg.xml b/packages/SystemUI/res/drawable/dialog_tri_state_navigation_bg.xml new file mode 100644 index 000000000000..93011a323a5b --- /dev/null +++ b/packages/SystemUI/res/drawable/dialog_tri_state_navigation_bg.xml @@ -0,0 +1,29 @@ + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/dialog_tri_state_triangle_right.xml b/packages/SystemUI/res/drawable/dialog_tri_state_triangle_right.xml new file mode 100644 index 000000000000..ab90683bea54 --- /dev/null +++ b/packages/SystemUI/res/drawable/dialog_tri_state_triangle_right.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/dialog_tri_state_triangle_top.xml b/packages/SystemUI/res/drawable/dialog_tri_state_triangle_top.xml new file mode 100644 index 000000000000..e8566465d61b --- /dev/null +++ b/packages/SystemUI/res/drawable/dialog_tri_state_triangle_top.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/dialog_tri_state_up_bg.xml b/packages/SystemUI/res/drawable/dialog_tri_state_up_bg.xml new file mode 100644 index 000000000000..69757a77ee86 --- /dev/null +++ b/packages/SystemUI/res/drawable/dialog_tri_state_up_bg.xml @@ -0,0 +1,29 @@ + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/layout/tri_state_dialog.xml b/packages/SystemUI/res/layout/tri_state_dialog.xml new file mode 100644 index 000000000000..177ed9b80521 --- /dev/null +++ b/packages/SystemUI/res/layout/tri_state_dialog.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/values/colt_dimens.xml b/packages/SystemUI/res/values/colt_dimens.xml index 1884acf4cf09..7e43e9a11aac 100644 --- a/packages/SystemUI/res/values/colt_dimens.xml +++ b/packages/SystemUI/res/values/colt_dimens.xml @@ -88,4 +88,36 @@ 36dp + + 8.0dip + 21.0px + 423.0px + 270.375px + 296.0px + 270.375px + 165.0px + @dimen/tri_state_down_dialog_padding_edge_deep + @dimen/tri_state_down_dialog_padding_edge_deep + 270.375px + 2.0dip + 126.0px + 63.0px + 126.0px + 14.0px + 34.125px + 189.0px + 63.0px + 63.0px + 63.0px + 63.0px + 0.0px + 63.0px + 63.0px + 63.0px + 63.0px + 63.0px + 0.0px + 63.0px + 63.0px + diff --git a/packages/SystemUI/res/values/colt_styles.xml b/packages/SystemUI/res/values/colt_styles.xml index 725519e977a2..aed47445df7c 100644 --- a/packages/SystemUI/res/values/colt_styles.xml +++ b/packages/SystemUI/res/values/colt_styles.xml @@ -52,4 +52,10 @@ @*android:color/accent_device_default_light false + + + diff --git a/packages/SystemUI/src/com/android/systemui/tristate/TriStateUiController.java b/packages/SystemUI/src/com/android/systemui/tristate/TriStateUiController.java new file mode 100644 index 000000000000..203522132a3e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tristate/TriStateUiController.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 CypherOS + * Copyright 2014-2019 Paranoid Android + * + * 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.tristate; + +import com.android.systemui.plugins.Plugin; +import com.android.systemui.plugins.VolumeDialog.Callback; +import com.android.systemui.plugins.annotations.DependsOn; +import com.android.systemui.plugins.annotations.ProvidesInterface; + +@DependsOn(target = Callback.class) +@ProvidesInterface(action = "com.android.systemui.action.PLUGIN_TRI_STATE_UI", version = 1) +public interface TriStateUiController extends Plugin { + + public interface UserActivityListener { + void onTriStateUserActivity(); + } +} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/tristate/TriStateUiControllerImpl.java b/packages/SystemUI/src/com/android/systemui/tristate/TriStateUiControllerImpl.java new file mode 100644 index 000000000000..6d0f6359fdbc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tristate/TriStateUiControllerImpl.java @@ -0,0 +1,476 @@ +/* + * Copyright (C) 2019 CypherOS + * Copyright 2014-2019 Paranoid Android + * + * 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.tristate; + +import static android.view.Surface.ROTATION_90; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; + +import android.app.Dialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.drawable.ColorDrawable; +import android.hardware.display.DisplayManagerGlobal; +import android.media.AudioManager; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.provider.Settings; +import android.util.Log; +import android.view.Display; +import android.view.OrientationEventListener; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager.LayoutParams; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.tristate.TriStateUiController; +import com.android.systemui.tristate.TriStateUiController.UserActivityListener; +import com.android.systemui.plugins.VolumeDialogController; +import com.android.systemui.plugins.VolumeDialogController.Callbacks; +import com.android.systemui.plugins.VolumeDialogController.State; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; + +public class TriStateUiControllerImpl implements ConfigurationListener, TriStateUiController { + + private static String TAG = "TriStateUiControllerImpl"; + + private static final int MSG_DIALOG_SHOW = 1; + private static final int MSG_DIALOG_DISMISS = 2; + private static final int MSG_RESET_SCHEDULE = 3; + private static final int MSG_STATE_CHANGE = 4; + + private static final int MODE_NORMAL = AudioManager.RINGER_MODE_NORMAL; + private static final int MODE_SILENT = AudioManager.RINGER_MODE_SILENT; + private static final int MODE_VIBRATE = AudioManager.RINGER_MODE_VIBRATE; + + private static final int TRI_STATE_UI_POSITION_LEFT = 0; + private static final int TRI_STATE_UI_POSITION_RIGHT = 1; + + private static final int DIALOG_TIMEOUT = 2000; + + private Context mContext; + private final VolumeDialogController mVolumeDialogController; + private final Callbacks mVolumeDialogCallback = new Callbacks() { + @Override + public void onShowRequested(int reason) { } + + @Override + public void onDismissRequested(int reason) { } + + @Override + public void onScreenOff() { } + + @Override + public void onStateChanged(State state) { } + + @Override + public void onLayoutDirectionChanged(int layoutDirection) { } + + @Override + public void onShowVibrateHint() { } + + @Override + public void onShowSilentHint() { } + + @Override + public void onShowSafetyWarning(int flags) { } + + @Override + public void onAccessibilityModeChanged(Boolean showA11yStream) { } + + @Override + public void onCaptionComponentStateChanged( + Boolean isComponentEnabled, Boolean fromTooltip) {} + + @Override + public void onConfigurationChanged() { + updateTheme(); + updateTriStateLayout(); + } + }; + + private int mDensity; + private Dialog mDialog; + private int mDialogPosition; + private ViewGroup mDialogView; + private final H mHandler; + private UserActivityListener mListener; + OrientationEventListener mOrientationListener; + private int mOrientationType = 0; + private boolean mShowing = false; + private int mBackgroundColor = 0; + private int mThemeMode = 0; + private int mIconColor = 0; + private int mTextColor = 0; + private ImageView mTriStateIcon; + private TextView mTriStateText; + private int mTriStateMode = -1; + private Window mWindow; + private LayoutParams mWindowLayoutParams; + private int mWindowType; + + private final BroadcastReceiver mRingerStateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + updateRingerModeChanged(); + } + }; + + private final class H extends Handler { + private TriStateUiControllerImpl mUiController; + + public H(TriStateUiControllerImpl uiController) { + super(Looper.getMainLooper()); + mUiController = uiController; + } + + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_DIALOG_SHOW: + mUiController.handleShow(); + return; + case MSG_DIALOG_DISMISS: + mUiController.handleDismiss(); + return; + case MSG_RESET_SCHEDULE: + mUiController.handleResetTimeout(); + return; + case MSG_STATE_CHANGE: + mUiController.handleStateChanged(); + return; + default: + return; + } + } + } + + public TriStateUiControllerImpl(Context context) { + mContext = context; + mHandler = new H(this); + mOrientationListener = new OrientationEventListener(mContext, 3) { + @Override + public void onOrientationChanged(int orientation) { + checkOrientationType(); + } + }; + mVolumeDialogController = (VolumeDialogController) Dependency.get(VolumeDialogController.class); + IntentFilter ringerChanged = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION); + mContext.registerReceiver(mRingerStateReceiver, ringerChanged); + } + + private void checkOrientationType() { + Display display = DisplayManagerGlobal.getInstance().getRealDisplay(0); + if (display != null) { + int rotation = display.getRotation(); + if (rotation != mOrientationType) { + mOrientationType = rotation; + updateTriStateLayout(); + } + } + } + + public void init(int windowType, UserActivityListener listener) { + mWindowType = windowType; + mDensity = mContext.getResources().getConfiguration().densityDpi; + mListener = listener; + ((ConfigurationController) Dependency.get(ConfigurationController.class)).addCallback(this); + mVolumeDialogController.addCallback(mVolumeDialogCallback, mHandler); + initDialog(); + } + + public void destroy() { + ((ConfigurationController) Dependency.get(ConfigurationController.class)).removeCallback(this); + mVolumeDialogController.removeCallback(mVolumeDialogCallback); + mContext.unregisterReceiver(mRingerStateReceiver); + } + + private void initDialog() { + mDialog = new Dialog(mContext); + mShowing = false; + mWindow = mDialog.getWindow(); + mWindow.requestFeature(Window.FEATURE_NO_TITLE); + mWindow.setBackgroundDrawable(new ColorDrawable(0)); + mWindow.clearFlags(LayoutParams.FLAG_DIM_BEHIND); + mWindow.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE + | LayoutParams.FLAG_LAYOUT_IN_SCREEN + | LayoutParams.FLAG_NOT_TOUCH_MODAL + | LayoutParams.FLAG_SHOW_WHEN_LOCKED + | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | LayoutParams.FLAG_HARDWARE_ACCELERATED); + mDialog.setCanceledOnTouchOutside(false); + mWindowLayoutParams = mWindow.getAttributes(); + mWindowLayoutParams.type = mWindowType; + mWindowLayoutParams.format = -3; + mWindowLayoutParams.setTitle(TriStateUiControllerImpl.class.getSimpleName()); + mWindowLayoutParams.gravity = 53; + mWindowLayoutParams.y = mDialogPosition; + mWindow.setAttributes(mWindowLayoutParams); + mWindow.setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING); + mDialog.setContentView(R.layout.tri_state_dialog); + mDialogView = (ViewGroup) mDialog.findViewById(R.id.tri_state_layout); + mTriStateIcon = (ImageView) mDialog.findViewById(R.id.tri_state_icon); + mTriStateText = (TextView) mDialog.findViewById(R.id.tri_state_text); + updateTheme(); + } + + public void show() { + mHandler.obtainMessage(MSG_DIALOG_SHOW, 0, 0).sendToTarget(); + } + + private void registerOrientationListener(boolean enable) { + if (mOrientationListener.canDetectOrientation() && enable) { + Log.v(TAG, "Can detect orientation"); + mOrientationListener.enable(); + return; + } + Log.v(TAG, "Cannot detect orientation"); + mOrientationListener.disable(); + } + + private void updateTriStateLayout() { + if (mContext != null) { + int iconId = 0; + int textId = 0; + int bg = 0; + Resources res = mContext.getResources(); + if (res != null) { + int positionY; + int positionY2 = mWindowLayoutParams.y; + int positionX = mWindowLayoutParams.x; + int gravity = mWindowLayoutParams.gravity; + switch (mTriStateMode) { + case MODE_SILENT: + iconId = R.drawable.ic_volume_ringer_mute; + textId = R.string.volume_ringer_status_silent; + break; + case MODE_VIBRATE: + iconId = R.drawable.ic_volume_ringer_vibrate; + textId = R.string.volume_ringer_status_vibrate; + break; + case MODE_NORMAL: + iconId = R.drawable.ic_volume_ringer; + textId = R.string.volume_ringer_status_normal; + break; + } + int triStatePos = res.getInteger(com.android.internal.R.integer.config_alertSliderLocation); + boolean isTsKeyRight = true; + if (triStatePos == TRI_STATE_UI_POSITION_LEFT) { + isTsKeyRight = false; + } else if (triStatePos == TRI_STATE_UI_POSITION_RIGHT) { + isTsKeyRight = true; + } + switch (mOrientationType) { + case ROTATION_90: + if (isTsKeyRight) { + gravity = 51; + } else { + gravity = 83; + } + positionY2 = res.getDimensionPixelSize(R.dimen.tri_state_up_dialog_position_deep_land); + if (isTsKeyRight) { + positionY2 += res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + } + if (mTriStateMode == MODE_SILENT) { + positionX = res.getDimensionPixelSize(R.dimen.tri_state_up_dialog_position_l); + } else if (mTriStateMode == MODE_VIBRATE) { + positionX = res.getDimensionPixelSize(R.dimen.tri_state_middle_dialog_position_l); + } else if (mTriStateMode == MODE_NORMAL) { + positionX = res.getDimensionPixelSize(R.dimen.tri_state_down_dialog_position_l); + } + bg = R.drawable.dialog_tri_state_middle_bg; + break; + case ROTATION_180: + if (isTsKeyRight) { + gravity = 83; + } else { + gravity = 85; + } + positionX = res.getDimensionPixelSize(R.dimen.tri_state_up_dialog_position_deep); + if (mTriStateMode != MODE_SILENT) { + if (mTriStateMode != MODE_VIBRATE) { + if (mTriStateMode == MODE_NORMAL) { + positionY = res.getDimensionPixelSize(R.dimen.tri_state_down_dialog_position) + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + } + bg = R.drawable.dialog_tri_state_middle_bg; + break; + } + positionY = res.getDimensionPixelSize(R.dimen.tri_state_middle_dialog_position) + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + } else { + positionY = res.getDimensionPixelSize(R.dimen.tri_state_up_dialog_position) + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + } + positionY2 = positionY; + bg = R.drawable.dialog_tri_state_middle_bg; + case ROTATION_270: + if (isTsKeyRight) { + gravity = 85; + } else { + gravity = 53; + } + positionY2 = res.getDimensionPixelSize(R.dimen.tri_state_up_dialog_position_deep_land); + if (!isTsKeyRight) { + positionY2 += res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + } + if (mTriStateMode == MODE_SILENT) { + positionX = res.getDimensionPixelSize(R.dimen.tri_state_up_dialog_position_l); + } else if (mTriStateMode == MODE_VIBRATE) { + positionX = res.getDimensionPixelSize(R.dimen.tri_state_middle_dialog_position_l); + } else if (mTriStateMode == MODE_NORMAL) { + positionX = res.getDimensionPixelSize(R.dimen.tri_state_down_dialog_position_l); + } + bg = R.drawable.dialog_tri_state_middle_bg; + break; + default: + if (isTsKeyRight) { + gravity = 53; + } else { + gravity = 51; + } + positionX = res.getDimensionPixelSize(R.dimen.tri_state_up_dialog_position_deep); + if (mTriStateMode != MODE_SILENT) { + if (mTriStateMode != MODE_VIBRATE) { + if (mTriStateMode == MODE_NORMAL) { + positionY2 = res.getDimensionPixelSize(R.dimen.tri_state_down_dialog_position) + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + bg = R.drawable.dialog_tri_state_down_bg; + break; + } + } + positionY2 = res.getDimensionPixelSize(R.dimen.tri_state_middle_dialog_position) + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + bg = R.drawable.dialog_tri_state_middle_bg; + break; + } + positionY2 = res.getDimensionPixelSize(R.dimen.tri_state_up_dialog_position) + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + bg = R.drawable.dialog_tri_state_up_bg; + break; + } + if (mTriStateMode != -1) { + if (mTriStateIcon != null) { + mTriStateIcon.setImageResource(iconId); + } + if (mTriStateText != null) { + String inputText = res.getString(textId); + if (inputText != null && mTriStateText.length() == inputText.length()) { + StringBuilder sb = new StringBuilder(); + sb.append(inputText); + sb.append(" "); + inputText = sb.toString(); + } + mTriStateText.setText(inputText); + } + if (mDialogView != null) { + mDialogView.setBackgroundDrawable(res.getDrawable(bg)); + } + mDialogPosition = positionY2; + } + positionY = res.getDimensionPixelSize(R.dimen.tri_state_dialog_padding); + mWindowLayoutParams.gravity = gravity; + mWindowLayoutParams.y = positionY2 - positionY; + mWindowLayoutParams.x = positionX - positionY; + mWindow.setAttributes(mWindowLayoutParams); + handleResetTimeout(); + } + } + } + + private void updateRingerModeChanged() { + mHandler.obtainMessage(MSG_STATE_CHANGE, 0, 0).sendToTarget(); + if (mTriStateMode != -1) { + show(); + } + } + + private void handleShow() { + mHandler.removeMessages(MSG_DIALOG_SHOW); + mHandler.removeMessages(MSG_DIALOG_DISMISS); + handleResetTimeout(); + if (!mShowing) { + updateTheme(); + registerOrientationListener(true); + checkOrientationType(); + mShowing = true; + mDialog.show(); + if (mListener != null) { + mListener.onTriStateUserActivity(); + } + } + } + + private void handleDismiss() { + mHandler.removeMessages(MSG_DIALOG_SHOW); + mHandler.removeMessages(MSG_DIALOG_DISMISS); + if (mShowing) { + registerOrientationListener(false); + mShowing = false; + mDialog.dismiss(); + } + } + + private void handleStateChanged() { + AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + int ringerMode = am.getRingerModeInternal(); + if (ringerMode != mTriStateMode) { + mTriStateMode = ringerMode; + updateTriStateLayout(); + if (mListener != null) { + mListener.onTriStateUserActivity(); + } + } + } + + public void handleResetTimeout() { + mHandler.removeMessages(MSG_DIALOG_DISMISS); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_DIALOG_DISMISS, MSG_RESET_SCHEDULE, 0), (long) DIALOG_TIMEOUT); + if (mListener != null) { + mListener.onTriStateUserActivity(); + } + } + + @Override + public void onDensityOrFontScaleChanged() { + handleDismiss(); + initDialog(); + updateTriStateLayout(); + } + + private void updateTheme() { + // Todo: Add some logic to update the theme only when a new theme is applied + mIconColor = getAttrColor(android.R.attr.colorAccent); + mTextColor = getAttrColor(android.R.attr.textColorPrimary); + mBackgroundColor = getAttrColor(android.R.attr.colorPrimary); + mDialogView.setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor)); + mTriStateIcon.setColorFilter(mIconColor); + mTriStateText.setTextColor(mTextColor); + } + + public int getAttrColor(int attr) { + TypedArray ta = mContext.obtainStyledAttributes(new int[]{attr}); + int colorAccent = ta.getColor(0, 0); + ta.recycle(); + return colorAccent; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java index d2f185a88bfd..55de6c8bd8af 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java @@ -28,6 +28,8 @@ import com.android.settingslib.applications.InterestingConfigChanges; import com.android.systemui.Dependency; import com.android.systemui.SystemUI; +import com.android.systemui.tristate.TriStateUiController; +import com.android.systemui.tristate.TriStateUiControllerImpl; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.PluginDependencyProvider; @@ -44,7 +46,7 @@ * Implementation of VolumeComponent backed by the new volume dialog. */ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tunable, - VolumeDialogControllerImpl.UserActivityListener{ + VolumeDialogControllerImpl.UserActivityListener, TriStateUiController.UserActivityListener { public static final String VOLUME_DOWN_SILENT = "sysui_volume_down_silent"; public static final String VOLUME_UP_SILENT = "sysui_volume_up_silent"; @@ -57,6 +59,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna private final SystemUI mSysui; protected final Context mContext; private final VolumeDialogControllerImpl mController; + private TriStateUiControllerImpl mTriStateController; private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges( ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_ASSETS_PATHS | ActivityInfo.CONFIG_UI_MODE); @@ -73,6 +76,8 @@ public VolumeDialogComponent(SystemUI sysui, Context context) { mContext = context; mController = (VolumeDialogControllerImpl) Dependency.get(VolumeDialogController.class); mController.setUserActivityListener(this); + boolean hasAlertSlider = mContext.getResources(). + getBoolean(com.android.internal.R.bool.config_hasAlertSlider); // Allow plugins to reference the VolumeDialogController. Dependency.get(PluginDependencyProvider.class) .allowPluginDependency(VolumeDialogController.class); @@ -85,6 +90,13 @@ public VolumeDialogComponent(SystemUI sysui, Context context) { } mDialog = dialog; mDialog.init(LayoutParams.TYPE_VOLUME_OVERLAY, mVolumeDialogCallback); + if (hasAlertSlider) { + if (mTriStateController != null) { + mTriStateController.destroy(); + } + mTriStateController = new TriStateUiControllerImpl(mContext); + mTriStateController.init(LayoutParams.TYPE_VOLUME_OVERLAY, this); + } }).build(); applyConfiguration(); Dependency.get(TunerService.class).addTunable(this, VOLUME_DOWN_SILENT, VOLUME_UP_SILENT, @@ -176,6 +188,11 @@ private void startSettings(Intent intent) { true /* onlyProvisioned */, true /* dismissShade */); } + @Override + public void onTriStateUserActivity() { + onUserActivity(); + } + private final VolumeDialogImpl.Callback mVolumeDialogCallback = new VolumeDialogImpl.Callback() { @Override public void onZenSettingsClicked() {