diff --git a/core/res/res/drawable/work_widget_mask_view_background.xml b/core/res/res/drawable/work_widget_mask_view_background.xml deleted file mode 100644 index 17f0dbc2b91c8..0000000000000 --- a/core/res/res/drawable/work_widget_mask_view_background.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - diff --git a/core/res/res/layout/work_widget_mask_view.xml b/core/res/res/layout/work_widget_mask_view.xml index ce86ddc61876b..3c4770552383a 100644 --- a/core/res/res/layout/work_widget_mask_view.xml +++ b/core/res/res/layout/work_widget_mask_view.xml @@ -15,20 +15,24 @@ Copyright (C) 2015 The Android Open Source Project limitations under the License. --> + android:background="#F3374248" + android:clickable="true" > + android:layout_gravity="center" + android:clickable="false" /> - + android:src="@drawable/ic_corp_badge_off" + android:clickable="false" /> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 44a7a8d39077d..eba2e6a243fd1 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2431,8 +2431,9 @@ + - + diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 9a9c2bc200843..8603981ec88ab 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -16,9 +16,14 @@ package com.android.server.appwidget; +import static android.content.Context.KEYGUARD_SERVICE; +import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; + import android.app.AlarmManager; import android.app.AppGlobals; import android.app.AppOpsManager; +import android.app.KeyguardManager; import android.app.PendingIntent; import android.app.admin.DevicePolicyManagerInternal; import android.app.admin.DevicePolicyManagerInternal.OnCrossProfileWidgetProvidersChangeListener; @@ -72,10 +77,12 @@ import android.util.SparseIntArray; import android.util.TypedValue; import android.util.Xml; import android.view.Display; +import android.view.View; import android.view.WindowManager; import android.widget.RemoteViews; import com.android.internal.R; +import com.android.internal.app.UnlaunchableAppActivity; import com.android.internal.appwidget.IAppWidgetHost; import com.android.internal.appwidget.IAppWidgetService; import com.android.internal.os.BackgroundThread; @@ -146,7 +153,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); if (DEBUG) { - Slog.i(TAG, "Received broadcast: " + action); + Slog.i(TAG, "Received broadcast: " + action + " on user " + userId); } if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { @@ -156,10 +163,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } else if (Intent.ACTION_USER_STOPPED.equals(action)) { onUserStopped(userId); } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { - reloadWidgetsMaskedStateForUser(userId); + reloadWidgetsMaskedStateForGroup(userId); } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED.equals(action)) { synchronized (mLock) { - reloadWidgetProfileUnavailableMaskedStateLocked(userId); + reloadWidgetsMaskedState(userId); } } else if (Intent.ACTION_PACKAGES_SUSPENDED.equals(action)) { String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); @@ -202,6 +209,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private final AlarmManager mAlarmManager; private final UserManager mUserManager; private final AppOpsManager mAppOpsManager; + private final KeyguardManager mKeyguardManager; private final SecurityPolicy mSecurityPolicy; @@ -223,6 +231,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); + mKeyguardManager = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE); mSaveStateHandler = BackgroundThread.getHandler(); mCallbackHandler = new CallbackHandler(mContext.getMainLooper()); mBackupRestoreController = new BackupRestoreController(); @@ -436,48 +445,51 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku /** * Reload all widgets' masked state for the given user and its associated profiles, including * due to user not being available and package suspension. + * userId must be the group parent. */ - private void reloadWidgetsMaskedStateForUser(int userId) { - if (!mUserManager.isUserUnlocked(userId) || - isProfileWithLockedParent(userId)) { + private void reloadWidgetsMaskedStateForGroup(int userId) { + if (!mUserManager.isUserUnlocked(userId)) { return; } synchronized (mLock) { - reloadWidgetPackageSuspensionMaskedStateLocked(userId); + reloadWidgetsMaskedState(userId); List profiles = mUserManager.getEnabledProfiles(userId); if (profiles != null) { for (int i = 0; i < profiles.size(); i++) { UserInfo user = profiles.get(i); - reloadWidgetProfileUnavailableMaskedStateLocked(user.id); - reloadWidgetPackageSuspensionMaskedStateLocked(user.id); + reloadWidgetsMaskedState(user.id); } } } } - /** - * Mask/unmask widgets in the given profile, depending on the quiet state - * or locked state of the profile. - */ - private void reloadWidgetProfileUnavailableMaskedStateLocked(int profileId) { + private void reloadWidgetsMaskedState(int userId) { final long identity = Binder.clearCallingIdentity(); try { - if (!isProfileWithUnlockedParent(profileId)) { - return; - } - UserInfo user = mUserManager.getUserInfo(profileId); - boolean shouldMask = user.isQuietModeEnabled() || - !mUserManager.isUserUnlocked(user.getUserHandle()); + UserInfo user = mUserManager.getUserInfo(userId); + + boolean lockedProfile = !mUserManager.isUserUnlocked(userId); + boolean quietProfile = user.isQuietModeEnabled(); final int N = mProviders.size(); for (int i = 0; i < N; i++) { Provider provider = mProviders.get(i); int providerUserId = provider.getUserId(); - if (providerUserId != profileId) { + if (providerUserId != userId) { continue; } - if (provider.setMaskedByProfileUnavailabledLocked(shouldMask)) { + + boolean changed = provider.setMaskedByLockedProfileLocked(lockedProfile); + changed |= provider.setMaskedByQuietProfileLocked(quietProfile); + try { + boolean suspended = mPackageManager.isPackageSuspendedForUser( + provider.info.provider.getPackageName(), provider.getUserId()); + changed |= provider.setMaskedBySuspendedPackageLocked(suspended); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to query application info", e); + } + if (changed) { if (provider.isMaskedLocked()) { - maskWidgetsViewsLocked(provider); + maskWidgetsViewsLocked(provider, null); } else { unmaskWidgetsViewsLocked(provider); } @@ -488,33 +500,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } - /** - * Reload widget's masked state due to package suspension state. - */ - private void reloadWidgetPackageSuspensionMaskedStateLocked(int profileId) { - final int N = mProviders.size(); - for (int i = 0; i < N; i++) { - Provider provider = mProviders.get(i); - int providerUserId = provider.getUserId(); - if (providerUserId != profileId) { - continue; - } - try { - boolean suspended = mPackageManager.isPackageSuspendedForUser( - provider.info.provider.getPackageName(), provider.getUserId()); - if (provider.setMaskedBySuspendedPackageLocked(suspended)) { - if (provider.isMaskedLocked()) { - maskWidgetsViewsLocked(provider); - } else { - unmaskWidgetsViewsLocked(provider); - } - } - } catch (RemoteException e) { - Slog.e(TAG, "Failed to query application info", e); - } - } - } - /** * Incrementally update the masked state due to package suspension state. */ @@ -535,7 +520,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } if (provider.setMaskedBySuspendedPackageLocked(suspended)) { if (provider.isMaskedLocked()) { - maskWidgetsViewsLocked(provider); + maskWidgetsViewsLocked(provider, null); } else { unmaskWidgetsViewsLocked(provider); } @@ -544,14 +529,13 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } - private Bitmap createMaskedWidgetBitmap(Provider provider) { + private Bitmap createMaskedWidgetBitmap(String providerPackage, int providerUserId) { final long identity = Binder.clearCallingIdentity(); try { // Load the unbadged application icon and pass it to the widget to appear on // the masked view. - final String providerPackage = provider.info.provider.getPackageName(); Context userContext = mContext.createPackageContextAsUser(providerPackage, 0, - UserHandle.of(provider.getUserId())); + UserHandle.of(providerUserId)); PackageManager pm = userContext.getPackageManager(); Drawable icon = pm.getApplicationInfo(providerPackage, 0).loadUnbadgedIcon(pm); // Create a bitmap of the icon which is what the widget's remoteview requires. @@ -566,18 +550,73 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } - private void maskWidgetsViewsLocked(Provider provider) { - Bitmap iconBitmap = createMaskedWidgetBitmap(provider); + private RemoteViews createMaskedWidgetRemoteViews(Bitmap icon, boolean showBadge, + PendingIntent onClickIntent) { + RemoteViews views = new RemoteViews(mContext.getPackageName(), + R.layout.work_widget_mask_view); + if (icon != null) { + views.setImageViewBitmap(R.id.work_widget_app_icon, icon); + } + if (!showBadge) { + views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE); + } + if (onClickIntent != null) { + views.setOnClickPendingIntent(R.id.work_widget_mask_frame, onClickIntent); + } + return views; + } + + /** + * Mask the target widget belonging to the specified provider, or all active widgets + * of the provider if target widget == null. + */ + private void maskWidgetsViewsLocked(Provider provider, Widget targetWidget) { + final int widgetCount = provider.widgets.size(); + if (widgetCount == 0) { + return; + } + final String providerPackage = provider.info.provider.getPackageName(); + final int providerUserId = provider.getUserId(); + Bitmap iconBitmap = createMaskedWidgetBitmap(providerPackage, providerUserId); if (iconBitmap == null) { return; } + final boolean showBadge; + final Intent onClickIntent; + if (provider.maskedBySuspendedPackage) { + final long identity = Binder.clearCallingIdentity(); + try { + UserInfo userInfo = mUserManager.getUserInfo(providerUserId); + showBadge = userInfo.isManagedProfile(); + onClickIntent = UnlaunchableAppActivity.createPackageSuspendedDialogIntent( + providerPackage, providerUserId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } else if (provider.maskedByQuietProfile) { + showBadge = true; + onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent( + providerUserId); + } else /* provider.maskedByLockedProfile */ { + showBadge = true; + onClickIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null, + providerUserId); + if (onClickIntent != null) { + onClickIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + } + } - final int widgetCount = provider.widgets.size(); for (int j = 0; j < widgetCount; j++) { Widget widget = provider.widgets.get(j); - if (widget.replaceWithMaskedViewsLocked(mContext, iconBitmap)) { - scheduleNotifyUpdateAppWidgetLocked(widget, - widget.getEffectiveViewsLocked()); + if (targetWidget != null && targetWidget != widget) continue; + PendingIntent intent = null; + if (onClickIntent != null) { + intent = PendingIntent.getActivity(mContext, widget.appWidgetId, + onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT); + } + RemoteViews views = createMaskedWidgetRemoteViews(iconBitmap, showBadge, intent); + if (widget.replaceWithMaskedViewsLocked(views)) { + scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked()); } } } @@ -587,8 +626,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku for (int j = 0; j < widgetCount; j++) { Widget widget = provider.widgets.get(j); if (widget.clearMaskedViewsLocked()) { - scheduleNotifyUpdateAppWidgetLocked(widget, - widget.getEffectiveViewsLocked()); + scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked()); } } } @@ -2472,7 +2510,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } synchronized (mLock) { ensureGroupStateLoadedLocked(userId); - reloadWidgetsMaskedStateForUser(userId); + reloadWidgetsMaskedStateForGroup(mSecurityPolicy.getGroupParent(userId)); final int N = mProviders.size(); for (int i = 0; i < N; i++) { @@ -2614,10 +2652,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku // If we are adding a widget it might be for a provider that // is currently masked, if so mask the widget. if (widget.provider.isMaskedLocked()) { - Bitmap bitmap = createMaskedWidgetBitmap(widget.provider); - if (bitmap != null) { - widget.replaceWithMaskedViewsLocked(mContext, bitmap); - } + maskWidgetsViewsLocked(widget.provider, widget); } else { widget.clearMaskedViewsLocked(); } @@ -3610,7 +3645,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku PendingIntent broadcast; boolean zombie; // if we're in safe mode, don't prune this just because nobody references it - boolean maskedByProfileUnavailable; + boolean maskedByLockedProfile; + boolean maskedByQuietProfile; boolean maskedBySuspendedPackage; int tag = TAG_UNDEFINED; // for use while saving state (the index) @@ -3642,22 +3678,29 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku return "Provider{" + id + (zombie ? " Z" : "") + '}'; } - // returns true if the provider's masked state is changed as a result - public boolean setMaskedByProfileUnavailabledLocked(boolean masked) { - boolean oldMaskedState = isMaskedLocked(); - maskedByProfileUnavailable = masked; - return isMaskedLocked() != oldMaskedState; + // returns true if it's different from previous state. + public boolean setMaskedByQuietProfileLocked(boolean masked) { + boolean oldState = maskedByQuietProfile; + maskedByQuietProfile = masked; + return masked != oldState; } - // returns true if the provider's masked state is changed as a result + // returns true if it's different from previous state. + public boolean setMaskedByLockedProfileLocked(boolean masked) { + boolean oldState = maskedByLockedProfile; + maskedByLockedProfile = masked; + return masked != oldState; + } + + // returns true if it's different from previous state. public boolean setMaskedBySuspendedPackageLocked(boolean masked) { - boolean oldMaskedState = isMaskedLocked(); + boolean oldState = maskedBySuspendedPackage; maskedBySuspendedPackage = masked; - return isMaskedLocked() != oldMaskedState; + return masked != oldState; } public boolean isMaskedLocked() { - return maskedByProfileUnavailable || maskedBySuspendedPackage; + return maskedByQuietProfile || maskedByLockedProfile || maskedBySuspendedPackage; } } @@ -3814,14 +3857,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku return "AppWidgetId{" + appWidgetId + ':' + host + ':' + provider + '}'; } - private boolean replaceWithMaskedViewsLocked(Context context, Bitmap icon) { - if (maskedViews != null) { - return false; - } - maskedViews = new RemoteViews(context.getPackageName(), R.layout.work_widget_mask_view); - if (icon != null) { - maskedViews.setImageViewBitmap(R.id.work_widget_app_icon, icon); - } + private boolean replaceWithMaskedViewsLocked(RemoteViews views) { + maskedViews = views; return true; }