diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java index 9a18880a353ba..965b6e0a02cd4 100644 --- a/core/java/android/accounts/Account.java +++ b/core/java/android/accounts/Account.java @@ -30,6 +30,7 @@ import com.android.internal.annotations.GuardedBy; +import java.util.Objects; import java.util.Set; /** @@ -85,6 +86,12 @@ public Account(String name, String type, String accessId) { if (TextUtils.isEmpty(type)) { throw new IllegalArgumentException("the type must not be empty: " + type); } + if (name.length() > 200) { + throw new IllegalArgumentException("account name is longer than 200 characters"); + } + if (type.length() > 200) { + throw new IllegalArgumentException("account type is longer than 200 characters"); + } this.name = name; this.type = type; this.accessId = accessId; diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java index d24694faff930..60cb563cca3df 100644 --- a/core/java/android/app/admin/DevicePolicyManagerInternal.java +++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java @@ -16,6 +16,7 @@ package android.app.admin; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.ComponentName; @@ -76,6 +77,13 @@ public interface OnCrossProfileWidgetProvidersChangeListener { public abstract void addOnCrossProfileWidgetProvidersChangeListener( OnCrossProfileWidgetProvidersChangeListener listener); + /** + * @param userHandle the handle of the user whose profile owner is being fetched. + * @return the configured supervision app if it exists and is the device owner or policy owner. + */ + public abstract @Nullable ComponentName getProfileOwnerOrDeviceOwnerSupervisionComponent( + @NonNull UserHandle userHandle); + /** * Checks if an app with given uid is an active device admin of its user and has the policy * specified. diff --git a/core/java/android/debug/AdbManager.java b/core/java/android/debug/AdbManager.java index 7714dd80f9100..243f801871850 100644 --- a/core/java/android/debug/AdbManager.java +++ b/core/java/android/debug/AdbManager.java @@ -38,6 +38,7 @@ public class AdbManager { * * @hide */ + @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) public static final String WIRELESS_DEBUG_STATE_CHANGED_ACTION = "com.android.server.adb.WIRELESS_DEBUG_STATUS"; @@ -46,6 +47,7 @@ public class AdbManager { * * @hide */ + @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) public static final String WIRELESS_DEBUG_PAIRED_DEVICES_ACTION = "com.android.server.adb.WIRELESS_DEBUG_PAIRED_DEVICES"; @@ -59,6 +61,7 @@ public class AdbManager { * * @hide */ + @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) public static final String WIRELESS_DEBUG_PAIRING_RESULT_ACTION = "com.android.server.adb.WIRELESS_DEBUG_PAIRING_RESULT"; diff --git a/core/java/com/android/internal/app/HarmfulAppWarningActivity.java b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java index ce2d229d41b32..33209e1101234 100644 --- a/core/java/com/android/internal/app/HarmfulAppWarningActivity.java +++ b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java @@ -16,6 +16,8 @@ package com.android.internal.app; +import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; + import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -27,6 +29,7 @@ import android.util.Log; import android.view.View; import android.widget.TextView; + import com.android.internal.R; /** @@ -48,6 +51,7 @@ public class HarmfulAppWarningActivity extends AlertActivity implements protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); final Intent intent = getIntent(); mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); mTarget = intent.getParcelableExtra(Intent.EXTRA_INTENT); diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java index 9f15d8991fa74..84391c1699415 100644 --- a/core/java/com/android/internal/infra/AndroidFuture.java +++ b/core/java/com/android/internal/infra/AndroidFuture.java @@ -16,23 +16,21 @@ package com.android.internal.infra; -import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; - import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Handler; -import android.os.Message; +import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; -import android.util.ExceptionUtils; +import android.util.EventLog; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; -import com.android.internal.util.function.pooled.PooledLambda; +import java.lang.reflect.Constructor; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; @@ -75,14 +73,16 @@ public class AndroidFuture extends CompletableFuture implements Parcelable private static final boolean DEBUG = false; private static final String LOG_TAG = AndroidFuture.class.getSimpleName(); + private static final Executor DIRECT_EXECUTOR = Runnable::run; private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0]; + private static @Nullable Handler sMainHandler; private final @NonNull Object mLock = new Object(); @GuardedBy("mLock") private @Nullable BiConsumer mListener; @GuardedBy("mLock") private @Nullable Executor mListenerExecutor = DIRECT_EXECUTOR; - private @NonNull Handler mTimeoutHandler = Handler.getMain(); + private @NonNull Handler mTimeoutHandler = getMainHandler(); private final @Nullable IAndroidFuture mRemoteOrigin; public AndroidFuture() { @@ -96,7 +96,7 @@ public AndroidFuture() { // Done if (in.readBoolean()) { // Failed - completeExceptionally(unparcelException(in)); + completeExceptionally(readThrowable(in)); } else { // Success complete((T) in.readValue(null)); @@ -108,6 +108,15 @@ public AndroidFuture() { } } + @NonNull + private static Handler getMainHandler() { + // This isn't thread-safe but we are okay with it. + if (sMainHandler == null) { + sMainHandler = new Handler(Looper.getMainLooper()); + } + return sMainHandler; + } + /** * Create a completed future with the given value. * @@ -236,9 +245,7 @@ private void callListenerAsync(BiConsumer listener if (mListenerExecutor == DIRECT_EXECUTOR) { callListener(listener, res, err); } else { - mListenerExecutor.execute(PooledLambda - .obtainRunnable(AndroidFuture::callListener, listener, res, err) - .recycleOnUse()); + mListenerExecutor.execute(() -> callListener(listener, res, err)); } } @@ -260,7 +267,8 @@ static void callListener( } else { // listener exception-case threw // give up on listener but preserve the original exception when throwing up - throw ExceptionUtils.appendCause(t, err); + t.addSuppressed(err); + throw t; } } } catch (Throwable t2) { @@ -272,9 +280,7 @@ static void callListener( /** @inheritDoc */ //@Override //TODO uncomment once java 9 APIs are exposed to frameworks public AndroidFuture orTimeout(long timeout, @NonNull TimeUnit unit) { - Message msg = PooledLambda.obtainMessage(AndroidFuture::triggerTimeout, this); - msg.obj = this; - mTimeoutHandler.sendMessageDelayed(msg, unit.toMillis(timeout)); + mTimeoutHandler.postDelayed(this::triggerTimeout, this, unit.toMillis(timeout)); return this; } @@ -507,7 +513,7 @@ public void writeToParcel(Parcel dest, int flags) { result = get(); } catch (Throwable t) { dest.writeBoolean(true); - parcelException(dest, unwrapExecutionException(t)); + writeThrowable(dest, unwrapExecutionException(t)); return; } dest.writeBoolean(false); @@ -545,45 +551,75 @@ Throwable unwrapExecutionException(Throwable t) { * Alternative to {@link Parcel#writeException} that stores the stack trace, in a * way consistent with the binder IPC exception propagation behavior. */ - private static void parcelException(Parcel p, @Nullable Throwable t) { - p.writeBoolean(t == null); - if (t == null) { + private static void writeThrowable(@NonNull Parcel parcel, @Nullable Throwable throwable) { + boolean hasThrowable = throwable != null; + parcel.writeBoolean(hasThrowable); + if (!hasThrowable) { + return; + } + + boolean isFrameworkParcelable = throwable instanceof Parcelable + && throwable.getClass().getClassLoader() == Parcelable.class.getClassLoader(); + parcel.writeBoolean(isFrameworkParcelable); + if (isFrameworkParcelable) { + parcel.writeParcelable((Parcelable) throwable, + Parcelable.PARCELABLE_WRITE_RETURN_VALUE); return; } - p.writeInt(Parcel.getExceptionCode(t)); - p.writeString(t.getClass().getName()); - p.writeString(t.getMessage()); - p.writeStackTrace(t); - parcelException(p, t.getCause()); + parcel.writeString(throwable.getClass().getName()); + parcel.writeString(throwable.getMessage()); + StackTraceElement[] stackTrace = throwable.getStackTrace(); + StringBuilder stackTraceBuilder = new StringBuilder(); + int truncatedStackTraceLength = Math.min(stackTrace != null ? stackTrace.length : 0, 5); + for (int i = 0; i < truncatedStackTraceLength; i++) { + if (i > 0) { + stackTraceBuilder.append('\n'); + } + stackTraceBuilder.append("\tat ").append(stackTrace[i]); + } + parcel.writeString(stackTraceBuilder.toString()); + writeThrowable(parcel, throwable.getCause()); } /** - * @see #parcelException + * @see #writeThrowable */ - private static @Nullable Throwable unparcelException(Parcel p) { - if (p.readBoolean()) { + private static @Nullable Throwable readThrowable(@NonNull Parcel parcel) { + final boolean hasThrowable = parcel.readBoolean(); + if (!hasThrowable) { return null; } - int exCode = p.readInt(); - String cls = p.readString(); - String msg = p.readString(); - String stackTrace = p.readInt() > 0 ? p.readString() : "\t"; - msg += "\n" + stackTrace; - - Exception ex = p.createExceptionOrNull(exCode, msg); - if (ex == null) { - ex = new RuntimeException(cls + ": " + msg); + boolean isFrameworkParcelable = parcel.readBoolean(); + if (isFrameworkParcelable) { + return parcel.readParcelable(Parcelable.class.getClassLoader()); } - ex.setStackTrace(EMPTY_STACK_TRACE); - Throwable cause = unparcelException(p); + String className = parcel.readString(); + String message = parcel.readString(); + String stackTrace = parcel.readString(); + String messageWithStackTrace = message + '\n' + stackTrace; + Throwable throwable; + try { + Class clazz = Class.forName(className, true, Parcelable.class.getClassLoader()); + if (Throwable.class.isAssignableFrom(clazz)) { + Constructor constructor = clazz.getConstructor(String.class); + throwable = (Throwable) constructor.newInstance(messageWithStackTrace); + } else { + android.util.EventLog.writeEvent(0x534e4554, "186530450", -1, ""); + throwable = new RuntimeException(className + ": " + messageWithStackTrace); + } + } catch (Throwable t) { + throwable = new RuntimeException(className + ": " + messageWithStackTrace); + throwable.addSuppressed(t); + } + throwable.setStackTrace(EMPTY_STACK_TRACE); + Throwable cause = readThrowable(parcel); if (cause != null) { - ex.initCause(ex); + throwable.initCause(cause); } - - return ex; + return throwable; } @Override diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java index 167d128a76f80..89869389cb597 100644 --- a/core/java/com/android/internal/infra/ServiceConnector.java +++ b/core/java/com/android/internal/infra/ServiceConnector.java @@ -26,14 +26,11 @@ import android.os.Handler; import android.os.IBinder; import android.os.IInterface; +import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; -import android.text.TextUtils; -import android.util.DebugUtils; import android.util.Log; -import com.android.internal.util.function.pooled.PooledLambda; - import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; @@ -47,7 +44,6 @@ import java.util.function.BiConsumer; import java.util.function.Function; - /** * Takes care of managing a {@link ServiceConnection} and auto-disconnecting from the service upon * a certain timeout. @@ -220,6 +216,7 @@ class Impl extends ArrayDeque> private final @NonNull Queue> mQueue = this; private final @NonNull List> mUnfinishedJobs = new ArrayList<>(); + private final @NonNull Handler mMainHandler = new Handler(Looper.getMainLooper()); private final @NonNull ServiceConnection mServiceConnection = this; private final @NonNull Runnable mTimeoutDisconnect = this; @@ -250,9 +247,8 @@ class Impl extends ArrayDeque> * {@link IInterface}. * Typically this is {@code IMyInterface.Stub::asInterface} */ - public Impl(@NonNull Context context, @NonNull Intent intent, - @Context.BindServiceFlags int bindingFlags, @UserIdInt int userId, - @Nullable Function binderAsInterface) { + public Impl(@NonNull Context context, @NonNull Intent intent, int bindingFlags, + @UserIdInt int userId, @Nullable Function binderAsInterface) { mContext = context; mIntent = intent; mBindingFlags = bindingFlags; @@ -264,7 +260,7 @@ public Impl(@NonNull Context context, @NonNull Intent intent, * {@link Handler} on which {@link Job}s will be called */ protected Handler getJobHandler() { - return Handler.getMain(); + return mMainHandler; } /** @@ -391,8 +387,7 @@ private void enqueue(@NonNull CompletionAwareJob task) { private boolean enqueue(@NonNull Job job) { cancelTimeout(); - return getJobHandler().sendMessage(PooledLambda.obtainMessage( - ServiceConnector.Impl::enqueueJobThread, this, job)); + return getJobHandler().post(() -> enqueueJobThread(job)); } void enqueueJobThread(@NonNull Job job) { @@ -422,7 +417,7 @@ private void cancelTimeout() { if (DEBUG) { logTrace(); } - Handler.getMain().removeCallbacks(mTimeoutDisconnect); + mMainHandler.removeCallbacks(mTimeoutDisconnect); } void completeExceptionally(@NonNull Job job, @NonNull Throwable ex) { @@ -486,7 +481,7 @@ private void scheduleUnbindTimeout() { } long timeout = getAutoDisconnectTimeoutMs(); if (timeout > 0) { - Handler.getMain().postDelayed(mTimeoutDisconnect, timeout); + mMainHandler.postDelayed(mTimeoutDisconnect, timeout); } else if (DEBUG) { Log.i(LOG_TAG, "Not scheduling unbind for permanently bound " + this); } @@ -502,7 +497,7 @@ public void unbind() { logTrace(); } mUnbinding = true; - getJobHandler().sendMessage(PooledLambda.obtainMessage(Impl::unbindJobThread, this)); + getJobHandler().post(this::unbindJobThread); } void unbindJobThread() { @@ -659,10 +654,7 @@ private String stateToString() { } private void logTrace() { - Log.i(LOG_TAG, - TextUtils.join(" -> ", - DebugUtils.callersWithin(ServiceConnector.class, /* offset= */ 1)) - + "(" + this + ")"); + Log.i(LOG_TAG, "See stacktrace", new Throwable()); } /** diff --git a/core/java/com/android/internal/util/nad/PixelPropsUtils.java b/core/java/com/android/internal/util/nad/PixelPropsUtils.java index 0c0ac4d6cf923..b484f583ce29c 100644 --- a/core/java/com/android/internal/util/nad/PixelPropsUtils.java +++ b/core/java/com/android/internal/util/nad/PixelPropsUtils.java @@ -66,7 +66,7 @@ public class PixelPropsUtils { propsToChange.put("DEVICE", "redfin"); propsToChange.put("PRODUCT", "redfin"); propsToChange.put("MODEL", "Pixel 5"); - propsToChange.put("FINGERPRINT", "google/redfin/redfin:12/SQ1A.211205.008/7888514:user/release-keys"); + propsToChange.put("FINGERPRINT", "google/redfin/redfin:12/SP2A.220305.012/8177914:user/release-keys"); propsToChangePixelXL = new HashMap<>(); propsToChangePixelXL.put("BRAND", "google"); propsToChangePixelXL.put("MANUFACTURER", "Google"); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 49793d3d08453..280cc1841620c 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5074,6 +5074,10 @@ + + + diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java index 4e70cfc1a18a6..66dcfdd9b47a2 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java @@ -201,7 +201,6 @@ private void startListening() { filter.addAction(DISABLE_PLUGIN); filter.addDataScheme("package"); mContext.registerReceiver(this, filter, PluginInstanceManager.PLUGIN_PERMISSION, null); - mContext.registerReceiver(this, filter); filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED); mContext.registerReceiver(this, filter); } diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt index 977e46ac3b446..d2ded71487dcd 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt @@ -131,6 +131,12 @@ class ControlsProviderLifecycleManager( wrapper = null bindService(false) } + + override fun onNullBinding(name: ComponentName?) { + if (DEBUG) Log.d(TAG, "onNullBinding $name") + wrapper = null + context.unbindService(this) + } } private fun handlePendingServiceMethods() { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt index e15380b42a78f..f6b420ba57f3c 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt @@ -17,6 +17,7 @@ package com.android.systemui.controls.ui import android.app.Dialog +import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.pm.PackageManager @@ -74,7 +75,7 @@ class ControlActionCoordinatorImpl @Inject constructor( bouncerOrRun(Action(cvh.cws.ci.controlId, { cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK) if (cvh.usePanel()) { - showDialog(cvh, control.getAppIntent().getIntent()) + showDetail(cvh, control.getAppIntent()) } else { cvh.action(CommandAction(templateId)) } @@ -100,7 +101,7 @@ class ControlActionCoordinatorImpl @Inject constructor( // Long press snould only be called when there is valid control state, otherwise ignore cvh.cws.control?.let { cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) - showDialog(cvh, it.getAppIntent().getIntent()) + showDetail(cvh, it.getAppIntent()) } }, false /* blockable */)) } @@ -155,17 +156,17 @@ class ControlActionCoordinatorImpl @Inject constructor( bgExecutor.execute { vibrator.vibrate(effect) } } - private fun showDialog(cvh: ControlViewHolder, intent: Intent) { + private fun showDetail(cvh: ControlViewHolder, pendingIntent: PendingIntent) { bgExecutor.execute { - val activities: List = cvh.context.packageManager.queryIntentActivities( - intent, + val activities: List = context.packageManager.queryIntentActivities( + pendingIntent.getIntent(), PackageManager.MATCH_DEFAULT_ONLY ) uiExecutor.execute { // make sure the intent is valid before attempting to open the dialog if (activities.isNotEmpty()) { - dialog = DetailDialog(cvh, intent).also { + dialog = DetailDialog(cvh, pendingIntent).also { it.setOnDismissListener { _ -> dialog = null } it.show() } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt index 9ec14523a8093..a7f7eb7ffb440 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt @@ -16,8 +16,11 @@ package com.android.systemui.controls.ui +import android.app.ActivityOptions import android.app.ActivityView +import android.app.PendingIntent import android.app.Dialog +import android.content.ComponentName import android.content.Intent import android.provider.Settings import android.view.View @@ -37,9 +40,8 @@ import com.android.systemui.R */ class DetailDialog( val cvh: ControlViewHolder, - val intent: Intent + val pendingIntent: PendingIntent ) : Dialog(cvh.context, R.style.Theme_SystemUI_Dialog_Control_DetailPanel) { - companion object { private const val PANEL_TOP_OFFSET = "systemui.controls_panel_top_offset" /* @@ -49,18 +51,19 @@ class DetailDialog( private const val EXTRA_USE_PANEL = "controls.DISPLAY_IN_PANEL" } + private val fillInIntent = Intent().apply { + putExtra(EXTRA_USE_PANEL, true) + + // Apply flags to make behaviour match documentLaunchMode=always. + addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) + addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) + } + var activityView = ActivityView(context, null, 0, false) val stateCallback: ActivityView.StateCallback = object : ActivityView.StateCallback() { override fun onActivityViewReady(view: ActivityView) { - val launchIntent = Intent(intent) - launchIntent.putExtra(EXTRA_USE_PANEL, true) - - // Apply flags to make behaviour match documentLaunchMode=always. - launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) - launchIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) - - view.startActivity(launchIntent) + view.startActivity(pendingIntent, fillInIntent, ActivityOptions.makeBasic()) } override fun onActivityViewDestroyed(view: ActivityView) {} @@ -68,6 +71,12 @@ class DetailDialog( override fun onTaskRemovalStarted(taskId: Int) { dismiss() } + + override fun onTaskCreated(taskId: Int, name: ComponentName?) { + requireViewById(R.id.controls_activity_view).apply { + setAlpha(1f) + } + } } init { @@ -76,6 +85,7 @@ class DetailDialog( requireViewById(R.id.controls_activity_view).apply { addView(activityView) + setAlpha(0f) } requireViewById(R.id.control_detail_close).apply { @@ -86,7 +96,7 @@ class DetailDialog( setOnClickListener { v: View -> dismiss() context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) - v.context.startActivity(intent) + pendingIntent.send() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index 2bef355d59f31..e0e603f7514d7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -134,11 +134,11 @@ public int getState() { } @Override - public boolean setState(int state) { + public boolean setState(int state, boolean force) { if (state > MAX_STATE || state < MIN_STATE) { throw new IllegalArgumentException("Invalid state " + state); } - if (state == mState) { + if (!force && state == mState) { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java index 07b35502478f5..fc40077810ff1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java @@ -58,7 +58,19 @@ public interface SysuiStatusBarStateController extends StatusBarStateController * @param state see {@link StatusBarState} for valid options * @return {@code true} if the state changed, else {@code false} */ - boolean setState(int state); + default boolean setState(int state) { + return setState(state, false /* force */); + } + + /** + * Update the status bar state + * @param state see {@link StatusBarState} for valid options + * @param force whether to set the state even if it's the same as the current state. This will + * dispatch the state to all StatusBarStateListeners, ensuring that all listening + * components are reset to this state. + * @return {@code true} if the state was changed or set forcefully + */ + boolean setState(int state, boolean force); /** * Update the dozing state from {@link StatusBar}'s perspective diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index da2f381d2e741..191335a13b60e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -4010,6 +4010,10 @@ public boolean isFullScreenUserSwitcherState() { } boolean updateIsKeyguard() { + return updateIsKeyguard(false /* force */); + } + + boolean updateIsKeyguard(boolean force) { boolean wakeAndUnlocking = mBiometricUnlockController.getMode() == BiometricUnlockController.MODE_WAKE_AND_UNLOCK; @@ -4032,7 +4036,7 @@ boolean updateIsKeyguard() { showKeyguardImpl(); } } else { - return hideKeyguardImpl(); + return hideKeyguardImpl(force); } return false; } @@ -4163,11 +4167,11 @@ private void runLaunchTransitionEndRunnable() { /** * @return true if we would like to stay in the shade, false if it should go away entirely */ - public boolean hideKeyguardImpl() { + public boolean hideKeyguardImpl(boolean force) { mIsKeyguard = false; Trace.beginSection("StatusBar#hideKeyguard"); boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide(); - if (!(mStatusBarStateController.setState(StatusBarState.SHADE))) { + if (!(mStatusBarStateController.setState(StatusBarState.SHADE, force))) { //TODO: StatusBarStateController should probably know about hiding the keyguard and // notify listeners. @@ -4620,7 +4624,8 @@ public void onFinishedGoingToSleep() { // is correct. mHandler.post(() -> onCameraLaunchGestureDetected(mLastCameraLaunchSource)); } - updateIsKeyguard(); + // When finished going to sleep, force the status bar state to avoid stale state. + updateIsKeyguard(true /* force */); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt index 2d3757c29ebf0..12096bc06748d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt @@ -17,6 +17,9 @@ package com.android.systemui.controls.controller import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection import android.os.UserHandle import android.service.controls.IControlsActionCallback import android.service.controls.IControlsProvider @@ -43,6 +46,8 @@ import org.mockito.ArgumentMatchers.eq import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.`when` +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -57,8 +62,6 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { private lateinit var subscriberService: IControlsSubscriber.Stub @Mock private lateinit var service: IControlsProvider.Stub - @Mock - private lateinit var loadCallback: ControlsBindingController.LoadCallback @Captor private lateinit var wrapperCaptor: ArgumentCaptor @@ -75,7 +78,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) - mContext.addMockService(componentName, service) + context.addMockService(componentName, service) executor = FakeExecutor(FakeSystemClock()) `when`(service.asBinder()).thenCallRealMethod() `when`(service.queryLocalInterface(ArgumentMatchers.anyString())).thenReturn(service) @@ -98,7 +101,36 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { fun testBindService() { manager.bindService() executor.runAllReady() - assertTrue(mContext.isBound(componentName)) + assertTrue(context.isBound(componentName)) + } + + @Test + fun testNullBinding() { + val mockContext = mock(Context::class.java) + lateinit var serviceConnection: ServiceConnection + `when`(mockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer { + val component = (it.arguments[0] as Intent).component + if (component == componentName) { + serviceConnection = it.arguments[1] as ServiceConnection + serviceConnection.onNullBinding(component) + true + } else { + false + } + } + + val nullManager = ControlsProviderLifecycleManager( + mockContext, + executor, + actionCallbackService, + UserHandle.of(0), + componentName + ) + + nullManager.bindService() + executor.runAllReady() + + verify(mockContext).unbindService(serviceConnection) } @Test @@ -109,7 +141,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { manager.unbindService() executor.runAllReady() - assertFalse(mContext.isBound(componentName)) + assertFalse(context.isBound(componentName)) } @Test @@ -119,7 +151,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { verify(service).load(subscriberService) - assertTrue(mContext.isBound(componentName)) + assertTrue(context.isBound(componentName)) } @Test @@ -129,7 +161,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { manager.unbindService() executor.runAllReady() - assertFalse(mContext.isBound(componentName)) + assertFalse(context.isBound(componentName)) } @Test @@ -162,7 +194,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { manager.maybeBindAndSubscribe(list, subscriberService) executor.runAllReady() - assertTrue(mContext.isBound(componentName)) + assertTrue(context.isBound(componentName)) verify(service).subscribe(list, subscriberService) } @@ -173,7 +205,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { manager.maybeBindAndSendAction(controlId, action) executor.runAllReady() - assertTrue(mContext.isBound(componentName)) + assertTrue(context.isBound(componentName)) verify(service).action(eq(controlId), capture(wrapperCaptor), eq(actionCallbackService)) assertEquals(action, wrapperCaptor.getValue().getWrappedAction()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt new file mode 100644 index 0000000000000..0ad03d2b41b3b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.controls.ui + +import android.app.ActivityView +import android.app.PendingIntent +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.capture +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.eq +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.any +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class DetailDialogTest : SysuiTestCase() { + + @Mock + private lateinit var activityView: ActivityView + @Mock + private lateinit var controlViewHolder: ControlViewHolder + @Mock + private lateinit var pendingIntent: PendingIntent + @Captor + private lateinit var viewCaptor: ArgumentCaptor + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + } + + @Test + fun testPendingIntentIsUnModified() { + // GIVEN the dialog is created with a PendingIntent + val dialog = createDialog(pendingIntent) + + // WHEN the ActivityView is initialized + dialog.stateCallback.onActivityViewReady(capture(viewCaptor)) + + // THEN the PendingIntent used to call startActivity is unmodified by systemui + verify(viewCaptor.value).startActivity(eq(pendingIntent), any(), any()) + } + + private fun createDialog(pendingIntent: PendingIntent): DetailDialog { + return DetailDialog( + controlViewHolder, + pendingIntent + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 5a08c9ca017b8..d528f209cd1ca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -809,12 +809,14 @@ public void testShowKeyguardImplementation_setsState() { // By default, showKeyguardImpl sets state to KEYGUARD. mStatusBar.showKeyguardImpl(); - verify(mStatusBarStateController).setState(eq(StatusBarState.KEYGUARD)); + verify(mStatusBarStateController).setState( + eq(StatusBarState.KEYGUARD), eq(false) /* force */); // If useFullscreenUserSwitcher is true, state is set to FULLSCREEN_USER_SWITCHER. when(mUserSwitcherController.useFullscreenUserSwitcher()).thenReturn(true); mStatusBar.showKeyguardImpl(); - verify(mStatusBarStateController).setState(eq(StatusBarState.FULLSCREEN_USER_SWITCHER)); + verify(mStatusBarStateController).setState( + eq(StatusBarState.FULLSCREEN_USER_SWITCHER), eq(false) /* force */); } @Test diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 9c25412f0c839..4df1bb5a156fb 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -1821,6 +1821,11 @@ private boolean addAccountInternal(UserAccounts accounts, Account account, Strin + ", skipping since the account already exists"); return false; } + if (accounts.accountsDb.findAllDeAccounts().size() > 100) { + Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString() + + ", skipping since more than 50 accounts on device exist"); + return false; + } long accountId = accounts.accountsDb.insertCeAccount(account, password); if (accountId < 0) { Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString() diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java index ed83a644cbfb2..95da946496e86 100644 --- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java +++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java @@ -18,6 +18,7 @@ import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull; +import android.annotation.NonNull; import android.annotation.TestApi; import android.app.ActivityManager; import android.app.Notification; @@ -171,6 +172,12 @@ protected AdbDebuggingManager(Context context, String confirmComponent, File tes mAdbConnectionInfo = new AdbConnectionInfo(); } + static void sendBroadcastWithDebugPermission(@NonNull Context context, @NonNull Intent intent, + @NonNull UserHandle userHandle) { + context.sendBroadcastAsUser(intent, userHandle, + android.Manifest.permission.MANAGE_DEBUGGING); + } + class PairingThread extends Thread implements NsdManager.RegistrationListener { private NsdManager mNsdManager; private String mPublicKey; @@ -1279,7 +1286,7 @@ private void sendServerConnectionState(boolean connected, int port) { ? AdbManager.WIRELESS_STATUS_CONNECTED : AdbManager.WIRELESS_STATUS_DISCONNECTED); intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL); } private void onAdbdWifiServerConnected(int port) { @@ -1351,7 +1358,8 @@ private void onPairingResult(String publicKey) { if (publicKey == null) { Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION); intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_FAIL); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, + UserHandle.ALL); } else { Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION); intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, @@ -1364,7 +1372,8 @@ private void onPairingResult(String publicKey) { } PairDevice device = new PairDevice(fingerprints, hostname, false); intent.putExtra(AdbManager.WIRELESS_PAIR_DEVICE_EXTRA, device); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, + UserHandle.ALL); // Add the key into the keystore mAdbKeyStore.setLastConnectionTime(publicKey, System.currentTimeMillis()); @@ -1378,14 +1387,14 @@ private void sendPairingPortToUI(int port) { intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_CONNECTED); intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL); } private void sendPairedDevicesToUI(Map devices) { Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION); // Map is not serializable, so need to downcast intent.putExtra(AdbManager.WIRELESS_DEVICES_EXTRA, (HashMap) devices); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL); } private void updateUIPairCode(String code) { @@ -1395,7 +1404,7 @@ private void updateUIPairCode(String code) { intent.putExtra(AdbManager.WIRELESS_PAIRING_CODE_EXTRA, code); intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_PAIRING_CODE); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL); } } diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java index 29bb5428dd84b..5b16daa5e835e 100644 --- a/services/core/java/com/android/server/adb/AdbService.java +++ b/services/core/java/com/android/server/adb/AdbService.java @@ -431,7 +431,7 @@ private void broadcastPortInfo(int port) { ? AdbManager.WIRELESS_STATUS_CONNECTED : AdbManager.WIRELESS_STATUS_DISCONNECTED); intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL); Slog.i(TAG, "sent port broadcast port=" + port); } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 1cd7115f8edcd..aa38fd1e6fc45 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1488,7 +1488,6 @@ must call startForeground() within a timeout anyway, so we don't need this final long delayMs = SystemClock.elapsedRealtime() - r.mLastSetFgsRestrictionTime; if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) { - resetFgsRestrictionLocked(r); setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.pid, r.appInfo.uid, r, false); EventLog.writeEvent(0x534e4554, "183147114", r.appInfo.uid, @@ -5046,5 +5045,6 @@ private void setFgsRestrictionLocked(String callingPackage, private void resetFgsRestrictionLocked(ServiceRecord r) { r.mAllowWhileInUsePermissionInFgs = false; + r.mLastSetFgsRestrictionTime = 0; } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index c30c06318ecb2..806c0118bc8d1 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2504,7 +2504,7 @@ private void maybeNotifyChannelOwner(String pkg, int uid, NotificationChannel pr } } - private void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group, + void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group, boolean fromApp, boolean fromListener) { Objects.requireNonNull(group); Objects.requireNonNull(pkg); @@ -3537,7 +3537,8 @@ public void deleteNotificationChannelGroup(String pkg, String groupId) { final int callingUid = Binder.getCallingUid(); NotificationChannelGroup groupToDelete = - mPreferencesHelper.getNotificationChannelGroup(groupId, pkg, callingUid); + mPreferencesHelper.getNotificationChannelGroupWithChannels( + pkg, callingUid, groupId, false); if (groupToDelete != null) { // Preflight for allowability final int userId = UserHandle.getUserId(callingUid); diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 5f14e5146bd03..7a93edeaf664f 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -788,6 +788,9 @@ public void createNotificationChannelGroup(String pkg, int uid, NotificationChan if (r == null) { throw new IllegalArgumentException("Invalid package"); } + if (fromTargetApp) { + group.setBlocked(false); + } final NotificationChannelGroup oldGroup = r.groups.get(group.getId()); if (oldGroup != null) { group.setChannels(oldGroup.getChannels()); diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 22c1126b6f959..013a86f96574f 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -134,6 +134,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements /** Upper bound on number of historical sessions for a UID */ private static final long MAX_HISTORICAL_SESSIONS = 1048576; + /** Destroy sessions older than this on storage free request */ + private static final long MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS = 8 * DateUtils.HOUR_IN_MILLIS; + /** * Allow verification-skipping if it's a development app installed through ADB with * disable verification flag specified. @@ -295,9 +298,7 @@ && getSession(session.getParentSessionId()) == null) { @GuardedBy("mSessions") private void reconcileStagesLocked(String volumeUuid) { - final File stagingDir = getTmpSessionDir(volumeUuid); - final ArraySet unclaimedStages = newArraySet( - stagingDir.listFiles(sStageFilter)); + final ArraySet unclaimedStages = getStagingDirsOnVolume(volumeUuid); // We also need to clean up orphaned staging directory for staged sessions final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid); @@ -308,9 +309,22 @@ private void reconcileStagesLocked(String volumeUuid) { final PackageInstallerSession session = mSessions.valueAt(i); unclaimedStages.remove(session.stageDir); } + removeStagingDirs(unclaimedStages); + } + + private ArraySet getStagingDirsOnVolume(String volumeUuid) { + final File stagingDir = getTmpSessionDir(volumeUuid); + final ArraySet stagingDirs = newArraySet(stagingDir.listFiles(sStageFilter)); + + // We also need to clean up orphaned staging directory for staged sessions + final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid); + stagingDirs.addAll(newArraySet(stagedSessionStagingDir.listFiles())); + return stagingDirs; + } + private void removeStagingDirs(ArraySet stagingDirsToRemove) { // Clean up orphaned staging directories - for (File stage : unclaimedStages) { + for (File stage : stagingDirsToRemove) { Slog.w(TAG, "Deleting orphan stage " + stage); synchronized (mPm.mInstallLock) { mPm.removeCodePathLI(stage); @@ -318,6 +332,38 @@ private void reconcileStagesLocked(String volumeUuid) { } } + /** + * Called to free up some storage space from obsolete installation files + */ + public void freeStageDirs(String volumeUuid) { + final ArraySet unclaimedStagingDirsOnVolume = getStagingDirsOnVolume(volumeUuid); + final long currentTimeMillis = System.currentTimeMillis(); + synchronized (mSessions) { + for (int i = 0; i < mSessions.size(); i++) { + final PackageInstallerSession session = mSessions.valueAt(i); + if (!unclaimedStagingDirsOnVolume.contains(session.stageDir)) { + // Only handles sessions stored on the target volume + continue; + } + final long age = currentTimeMillis - session.createdMillis; + if (age >= MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS) { + // Aggressively close old sessions because we are running low on storage + // Their staging dirs will be removed too + PackageInstallerSession root = !session.hasParentSessionId() + ? session : mSessions.get(session.getParentSessionId()); + if (!root.isDestroyed()) { + root.abandon(); + } + } else { + // Session is new enough, so it deserves to be kept even on low storage + unclaimedStagingDirsOnVolume.remove(session.stageDir); + } + } + } + removeStagingDirs(unclaimedStagingDirsOnVolume); + } + + public void onPrivateVolumeMounted(String volumeUuid) { synchronized (mSessions) { reconcileStagesLocked(volumeUuid); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index d690ae9e70f10..a253b509abb9e 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2638,14 +2638,8 @@ public void abandon() { List childSessions = getChildSessionsNotLocked(); synchronized (mLock) { - if (params.isStaged && mDestroyed) { - // If a user abandons staged session in an unsafe state, then system will try to - // abandon the destroyed staged session when it is safe on behalf of the user. - assertCallerIsOwnerOrRootOrSystemLocked(); - } else { - assertCallerIsOwnerOrRootLocked(); - } + assertCallerIsOwnerOrRootOrSystemLocked(); if (isStagedAndInTerminalState()) { // We keep the session in the database if it's in a finalized state. It will be // removed by PackageInstallerService when the last update time is old enough. diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 72786e77f42d4..6bd669b08dd70 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -5212,6 +5212,10 @@ public void freeStorage(String volumeUuid, long bytes, int storageFlags) throws InstantAppRegistry.DEFAULT_UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) { return; } + + // 12. Clear temp install session files + mInstallerService.freeStageDirs(volumeUuid); + } else { try { mInstaller.freeCache(volumeUuid, bytes, 0, 0); diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index 0ba7e41f676d5..1dc933b0fd203 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -409,6 +409,8 @@ static BasePermission createOrUpdate(PackageManagerInternal packageManagerIntern } if (bp.perm != null && Objects.equals(bp.perm.getPackageName(), p.getPackageName()) && Objects.equals(bp.perm.getName(), p.getName())) { + bp.perm.setFlags(bp.perm.getFlags() & ~PermissionInfo.FLAG_INSTALLED); + bp.perm = p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED); bp.protectionLevel = p.getProtectionLevel(); } if (bp.isRuntime() && (ownerChanged || wasNonRuntime)) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 6f6d77c935ce6..a13a503374937 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -56,6 +56,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; +import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; @@ -1570,6 +1571,7 @@ public int addWindow(Session session, IWindow client, int seq, final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid); + attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), callingUid, callingPid); res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid); if (res != WindowManagerGlobal.ADD_OKAY) { @@ -2154,6 +2156,7 @@ public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams if (attrs != null) { displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid); win.mToken.adjustWindowParams(win, attrs); + attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), uid, pid); // if they don't have the permission, mask out the status bar bits if (seq == win.mSeq) { int systemUiVisibility = attrs.systemUiVisibility @@ -8050,6 +8053,23 @@ void handleTaskFocusChange(Task task) { } } + /** + * You need ALLOW_SLIPPERY_TOUCHES permission to be able to set FLAG_SLIPPERY. + */ + private int sanitizeFlagSlippery(int flags, String windowName, int callingUid, int callingPid) { + if ((flags & FLAG_SLIPPERY) == 0) { + return flags; + } + final int permissionResult = mContext.checkPermission( + android.Manifest.permission.ALLOW_SLIPPERY_TOUCHES, callingPid, callingUid); + if (permissionResult != PackageManager.PERMISSION_GRANTED) { + Slog.w(TAG, "Removing FLAG_SLIPPERY from '" + windowName + + "' because it doesn't have ALLOW_SLIPPERY_TOUCHES permission"); + return flags & ~FLAG_SLIPPERY; + } + return flags; + } + /** * Assigns an InputChannel to a SurfaceControl and configures it to receive * touch input according to it's on-screen geometry. @@ -8087,8 +8107,9 @@ private void updateInputChannel(IBinder channelToken, int callingUid, int callin h.token = channelToken; h.name = name; - final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE - | LayoutParams.FLAG_SLIPPERY); + flags = sanitizeFlagSlippery(flags, name, callingUid, callingPid); + + final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE | FLAG_SLIPPERY); h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | sanitizedFlags; h.layoutParamsType = type; h.dispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index ad23be33dbd55..61640aa567461 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -9424,6 +9424,9 @@ && isProfileOwnerOfOrganizationOwnedDevice(userInfo.id)) { final ComponentName doComponent = mOwners.getDeviceOwnerComponent(); final ComponentName poComponent = mOwners.getProfileOwnerComponent(userHandle.getIdentifier()); + if (supervisorComponent == null) { + return null; + } if (supervisorComponent.equals(doComponent) || supervisorComponent.equals( poComponent)) { return supervisorComponent; @@ -12509,6 +12512,13 @@ public void addOnCrossProfileWidgetProvidersChangeListener( } } + @Override + public @Nullable ComponentName getProfileOwnerOrDeviceOwnerSupervisionComponent( + @NonNull UserHandle userHandle) { + return DevicePolicyManagerService.this.getProfileOwnerOrDeviceOwnerSupervisionComponent( + userHandle); + } + @Override public boolean isActiveAdminWithPolicy(int uid, int reqPolicy) { synchronized (getLockObject()) { diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java index cffff66b64f13..02cf971a80768 100644 --- a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java @@ -23,7 +23,14 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.debug.AdbManager; +import android.debug.IAdbManager; +import android.os.ServiceManager; import android.provider.Settings; import android.util.Log; @@ -105,6 +112,7 @@ public void setUp() throws Exception { public void tearDown() throws Exception { mKeyStore.deleteKeyStore(); setAllowedConnectionTime(mOriginalAllowedConnectionTime); + dropShellPermissionIdentity(); } /** @@ -813,6 +821,108 @@ private boolean isValidMdnsServiceName(String name) { return hasAtLeastOneLetter; } + CountDownLatch mAdbActionLatch = new CountDownLatch(1); + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + Log.i(TAG, "Received intent action=" + action); + if (AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION.equals(action)) { + assertEquals("Received broadcast without MANAGE_DEBUGGING permission.", + context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING), + PackageManager.PERMISSION_GRANTED); + Log.i(TAG, "action=" + action + " paired_device=" + intent.getSerializableExtra( + AdbManager.WIRELESS_DEVICES_EXTRA).toString()); + mAdbActionLatch.countDown(); + } else if (AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION.equals(action)) { + assertEquals("Received broadcast without MANAGE_DEBUGGING permission.", + context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING), + PackageManager.PERMISSION_GRANTED); + int status = intent.getIntExtra(AdbManager.WIRELESS_STATUS_EXTRA, + AdbManager.WIRELESS_STATUS_DISCONNECTED); + Log.i(TAG, "action=" + action + " status=" + status); + mAdbActionLatch.countDown(); + } else if (AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION.equals(action)) { + assertEquals("Received broadcast without MANAGE_DEBUGGING permission.", + context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING), + PackageManager.PERMISSION_GRANTED); + Integer res = intent.getIntExtra( + AdbManager.WIRELESS_STATUS_EXTRA, + AdbManager.WIRELESS_STATUS_FAIL); + Log.i(TAG, "action=" + action + " result=" + res); + + if (res.equals(AdbManager.WIRELESS_STATUS_PAIRING_CODE)) { + String pairingCode = intent.getStringExtra( + AdbManager.WIRELESS_PAIRING_CODE_EXTRA); + Log.i(TAG, "pairingCode=" + pairingCode); + } else if (res.equals(AdbManager.WIRELESS_STATUS_CONNECTED)) { + int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0); + Log.i(TAG, "port=" + port); + } + mAdbActionLatch.countDown(); + } + } + }; + + private void adoptShellPermissionIdentity() { + InstrumentationRegistry.getInstrumentation().getUiAutomation() + .adoptShellPermissionIdentity(android.Manifest.permission.MANAGE_DEBUGGING); + } + + private void dropShellPermissionIdentity() { + InstrumentationRegistry.getInstrumentation().getUiAutomation() + .dropShellPermissionIdentity(); + } + + @Test + public void testBroadcastReceiverWithPermissions() throws Exception { + adoptShellPermissionIdentity(); + final IAdbManager mAdbManager = IAdbManager.Stub.asInterface( + ServiceManager.getService(Context.ADB_SERVICE)); + IntentFilter intentFilter = + new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION); + intentFilter.addAction(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION); + intentFilter.addAction(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION); + assertEquals("Context does not have MANAGE_DEBUGGING permission.", + mContext.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING), + PackageManager.PERMISSION_GRANTED); + try { + mContext.registerReceiver(mReceiver, intentFilter); + mAdbManager.enablePairingByPairingCode(); + if (!mAdbActionLatch.await(TIMEOUT, TIMEOUT_TIME_UNIT)) { + fail("Receiver did not receive adb intent action within the timeout duration"); + } + } finally { + mContext.unregisterReceiver(mReceiver); + } + } + + @Test + public void testBroadcastReceiverWithoutPermissions() throws Exception { + adoptShellPermissionIdentity(); + final IAdbManager mAdbManager = IAdbManager.Stub.asInterface( + ServiceManager.getService(Context.ADB_SERVICE)); + IntentFilter intentFilter = + new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION); + intentFilter.addAction(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION); + intentFilter.addAction(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION); + mAdbManager.enablePairingByPairingCode(); + + dropShellPermissionIdentity(); + assertEquals("Context has MANAGE_DEBUGGING permission.", + mContext.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING), + PackageManager.PERMISSION_DENIED); + try { + mContext.registerReceiver(mReceiver, intentFilter); + + if (mAdbActionLatch.await(TIMEOUT, TIMEOUT_TIME_UNIT)) { + fail("Broadcast receiver received adb action intent without debug permissions"); + } + } finally { + mContext.unregisterReceiver(mReceiver); + } + } + /** * Runs an adb test with the provided configuration. * diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java old mode 100755 new mode 100644 index 1a4cce466c668..c0ecbbadb695e --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -179,6 +179,8 @@ import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; +import com.google.common.collect.ImmutableList; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -202,6 +204,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; @@ -245,6 +248,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Mock Resources mResources; @Mock + ActivityManagerInternal mAmi; + @Mock RankingHandler mRankingHandler; @Mock protected PackageManagerInternal mPackageManagerInternal; @@ -418,7 +423,6 @@ public void setUp() throws Exception { DeviceIdleInternal deviceIdleInternal = mock(DeviceIdleInternal.class); when(deviceIdleInternal.getNotificationWhitelistDuration()).thenReturn(3000L); - ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class); LocalServices.removeServiceForTest(UriGrantsManagerInternal.class); LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal); @@ -429,7 +433,7 @@ public void setUp() throws Exception { LocalServices.removeServiceForTest(DeviceIdleInternal.class); LocalServices.addService(DeviceIdleInternal.class, deviceIdleInternal); LocalServices.removeServiceForTest(ActivityManagerInternal.class); - LocalServices.addService(ActivityManagerInternal.class, activityManagerInternal); + LocalServices.addService(ActivityManagerInternal.class, mAmi); mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager); @@ -506,7 +510,7 @@ null, new ComponentName(PKG, "test_class"), mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, mAppOpsManager, mUm, mHistoryManager, mStatsManager, mock(TelephonyManager.class), - mock(ActivityManagerInternal.class)); + mAmi); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); mService.setAudioManager(mAudioManager); @@ -2473,7 +2477,8 @@ public void testDeleteChannelGroupNotifyListener() throws Exception { .thenReturn(associations); NotificationChannelGroup ncg = new NotificationChannelGroup("a", "b/c"); mService.setPreferencesHelper(mPreferencesHelper); - when(mPreferencesHelper.getNotificationChannelGroup(eq(ncg.getId()), eq(PKG), anyInt())) + when(mPreferencesHelper.getNotificationChannelGroupWithChannels( + eq(PKG), anyInt(), eq(ncg.getId()), anyBoolean())) .thenReturn(ncg); reset(mListeners); mBinderService.deleteNotificationChannelGroup(PKG, ncg.getId()); @@ -2482,6 +2487,56 @@ public void testDeleteChannelGroupNotifyListener() throws Exception { eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED)); } + @Test + public void testDeleteChannelGroupChecksForFgses() throws Exception { + List associations = new ArrayList<>(); + associations.add("a"); + when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid))) + .thenReturn(associations); + CountDownLatch latch = new CountDownLatch(2); + mService.createNotificationChannelGroup( + PKG, mUid, new NotificationChannelGroup("group", "group"), true, false); + new Thread(() -> { + NotificationChannel notificationChannel = new NotificationChannel("id", "id", + NotificationManager.IMPORTANCE_HIGH); + notificationChannel.setGroup("group"); + ParceledListSlice pls = + new ParceledListSlice(ImmutableList.of(notificationChannel)); + try { + mBinderService.createNotificationChannelsForPackage(PKG, mUid, pls); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + latch.countDown(); + }).start(); + new Thread(() -> { + try { + synchronized (this) { + wait(5000); + } + mService.createNotificationChannelGroup(PKG, mUid, + new NotificationChannelGroup("new", "new group"), true, false); + NotificationChannel notificationChannel = + new NotificationChannel("id", "id", NotificationManager.IMPORTANCE_HIGH); + notificationChannel.setGroup("new"); + ParceledListSlice pls = + new ParceledListSlice(ImmutableList.of(notificationChannel)); + try { + mBinderService.createNotificationChannelsForPackage(PKG, mUid, pls); + mBinderService.deleteNotificationChannelGroup(PKG, "group"); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } catch (Exception e) { + e.printStackTrace(); + } + latch.countDown(); + }).start(); + + latch.await(); + verify(mAmi).hasForegroundServiceNotification(anyString(), anyInt(), anyString()); + } + @Test public void testUpdateNotificationChannelFromPrivilegedListener_success() throws Exception { mService.setPreferencesHelper(mPreferencesHelper); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 6aeb40dae20ba..06cfbea72a824 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -2132,6 +2132,19 @@ public void testIsGroupBlocked_blocked() throws Exception { assertTrue(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId())); } + @Test + public void testIsGroupBlocked_appCannotCreateAsBlocked() throws Exception { + NotificationChannelGroup group = new NotificationChannelGroup("id", "name"); + group.setBlocked(true); + mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true); + assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId())); + + NotificationChannelGroup group3 = group.clone(); + group3.setBlocked(false); + mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group3, true); + assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId())); + } + @Test public void testIsGroup_appCannotResetBlock() throws Exception { NotificationChannelGroup group = new NotificationChannelGroup("id", "name"); @@ -3402,7 +3415,7 @@ public void testGetConversations_noConversations() { public void testGetConversations_noDisabledGroups() { NotificationChannelGroup group = new NotificationChannelGroup("a", "a"); group.setBlocked(true); - mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true); + mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, false); NotificationChannel parent = new NotificationChannel("parent", "p", 1); mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index a5b676012ea97..a54f263c32ae3 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -339,6 +339,7 @@ private void onUserUnlocked(int userId) { if (userId == UserHandle.USER_SYSTEM) { UsageStatsIdleService.scheduleUpdateMappingsJob(getContext()); } + final boolean deleteObsoleteData = shouldDeleteObsoleteData(UserHandle.of(userId)); synchronized (mLock) { // Create a user unlocked event to report final Event unlockEvent = new Event(USER_UNLOCKED, SystemClock.elapsedRealtime()); @@ -356,7 +357,7 @@ private void onUserUnlocked(int userId) { boolean needToFlush = !pendingEvents.isEmpty(); initializeUserUsageStatsServiceLocked(userId, System.currentTimeMillis(), - installedPackages); + installedPackages, deleteObsoleteData); mUserUnlockedStates.put(userId, true); final UserUsageStatsService userService = getUserUsageStatsServiceLocked(userId); if (userService == null) { @@ -551,13 +552,13 @@ private UserUsageStatsService getUserUsageStatsServiceLocked(int userId) { * when the user is initially unlocked. */ private void initializeUserUsageStatsServiceLocked(int userId, long currentTimeMillis, - HashMap installedPackages) { + HashMap installedPackages, boolean deleteObsoleteData) { final File usageStatsDir = new File(Environment.getDataSystemCeDirectory(userId), "usagestats"); final UserUsageStatsService service = new UserUsageStatsService(getContext(), userId, usageStatsDir, this); try { - service.init(currentTimeMillis, installedPackages); + service.init(currentTimeMillis, installedPackages, deleteObsoleteData); mUserState.put(userId, service); } catch (Exception e) { if (mUserManager.isUserUnlocked(userId)) { @@ -1029,6 +1030,10 @@ private boolean pruneUninstalledPackagesData(int userId) { * Called by the Binder stub. */ private boolean updatePackageMappingsData() { + // don't update the mappings if a profile user is defined + if (!shouldDeleteObsoleteData(UserHandle.SYSTEM)) { + return true; // return true so job scheduler doesn't reschedule the job + } // fetch the installed packages outside the lock so it doesn't block package manager. final HashMap installedPkgs = getInstalledPackages(UserHandle.USER_SYSTEM); synchronized (mLock) { @@ -1154,6 +1159,13 @@ UsageEvents queryEventsForPackage(int userId, long beginTime, long endTime, } } + private boolean shouldDeleteObsoleteData(UserHandle userHandle) { + final DevicePolicyManagerInternal dpmInternal = getDpmInternal(); + // If a profile owner is not defined for the given user, obsolete data should be deleted + return dpmInternal == null + || dpmInternal.getProfileOwnerOrDeviceOwnerSupervisionComponent(userHandle) == null; + } + private String buildFullToken(String packageName, String token) { final StringBuilder sb = new StringBuilder(packageName.length() + token.length() + 1); sb.append(packageName); @@ -2324,8 +2336,12 @@ public boolean updatePackageMappingsData() { private class MyPackageMonitor extends PackageMonitor { @Override public void onPackageRemoved(String packageName, int uid) { - mHandler.obtainMessage(MSG_PACKAGE_REMOVED, getChangingUserId(), 0, packageName) - .sendToTarget(); + final int changingUserId = getChangingUserId(); + // Only remove the package's data if a profile owner is not defined for the user + if (shouldDeleteObsoleteData(UserHandle.of(changingUserId))) { + mHandler.obtainMessage(MSG_PACKAGE_REMOVED, changingUserId, 0, packageName) + .sendToTarget(); + } super.onPackageRemoved(packageName, uid); } } diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index 26de11af6f4e9..0a5adc8e61d44 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -115,8 +115,9 @@ interface StatsUpdatedListener { mSystemTimeSnapshot = System.currentTimeMillis(); } - void init(final long currentTimeMillis, HashMap installedPackages) { - readPackageMappingsLocked(installedPackages); + void init(final long currentTimeMillis, HashMap installedPackages, + boolean deleteObsoleteData) { + readPackageMappingsLocked(installedPackages, deleteObsoleteData); mDatabase.init(currentTimeMillis); if (mDatabase.wasUpgradePerformed()) { mDatabase.prunePackagesDataOnUpgrade(installedPackages); @@ -180,12 +181,13 @@ int onPackageRemoved(String packageName, long timeRemoved) { return mDatabase.onPackageRemoved(packageName, timeRemoved); } - private void readPackageMappingsLocked(HashMap installedPackages) { + private void readPackageMappingsLocked(HashMap installedPackages, + boolean deleteObsoleteData) { mDatabase.readMappingsLocked(); // Package mappings for the system user are updated after 24 hours via a job scheduled by // UsageStatsIdleService to ensure restored data is not lost on first boot. Additionally, // this makes user service initialization a little quicker on subsequent boots. - if (mUserId != UserHandle.USER_SYSTEM) { + if (mUserId != UserHandle.USER_SYSTEM && deleteObsoleteData) { updatePackageMappingsLocked(installedPackages); } }