Merge "Add metadata to validate incoming administrator during transfer of ownership."
diff --git a/Android.bp b/Android.bp
index 2685ac3..defe655 100644
--- a/Android.bp
+++ b/Android.bp
@@ -324,6 +324,8 @@
"core/java/android/view/IOnKeyguardExitResult.aidl",
"core/java/android/view/IPinnedStackController.aidl",
"core/java/android/view/IPinnedStackListener.aidl",
+ "core/java/android/view/IRemoteAnimationRunner.aidl",
+ "core/java/android/view/IRemoteAnimationFinishedCallback.aidl",
"core/java/android/view/IRotationWatcher.aidl",
"core/java/android/view/IWallpaperVisibilityListener.aidl",
"core/java/android/view/IWindow.aidl",
diff --git a/api/current.txt b/api/current.txt
index 4db97a1..bfb1934 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6368,7 +6368,7 @@
method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
method public void addUserRestriction(android.content.ComponentName, java.lang.String);
method public boolean bindDeviceAdminServiceAsUser(android.content.ComponentName, android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
- method public boolean clearApplicationUserData(android.content.ComponentName, java.lang.String, android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener, java.util.concurrent.Executor);
+ method public boolean clearApplicationUserData(android.content.ComponentName, java.lang.String, java.util.concurrent.Executor, android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener);
method public void clearCrossProfileIntentFilters(android.content.ComponentName);
method public deprecated void clearDeviceOwnerApp(java.lang.String);
method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
@@ -7069,8 +7069,8 @@
public final class Slice implements android.os.Parcelable {
ctor protected Slice(android.os.Parcel);
- method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
- method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
+ method public static deprecated android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public static deprecated android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public int describeContents();
method public java.util.List<java.lang.String> getHints();
method public java.util.List<android.app.slice.SliceItem> getItems();
@@ -7156,7 +7156,10 @@
}
public class SliceManager {
+ method public android.app.slice.Slice bindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public android.app.slice.Slice bindSlice(android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public java.util.List<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri);
+ method public java.util.Collection<android.net.Uri> getSliceDescendants(android.net.Uri);
method public void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>);
method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, android.os.Handler);
@@ -7175,7 +7178,7 @@
method public final java.lang.String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
- method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri);
+ method public java.util.Collection<android.net.Uri> onGetSliceDescendants(android.net.Uri);
method public android.net.Uri onMapIntentToUri(android.content.Intent);
method public void onSlicePinned(android.net.Uri);
method public void onSliceUnpinned(android.net.Uri);
@@ -11157,7 +11160,7 @@
field public static final java.lang.String FEATURE_USB_HOST = "android.hardware.usb.host";
field public static final java.lang.String FEATURE_VERIFIED_BOOT = "android.software.verified_boot";
field public static final java.lang.String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking";
- field public static final java.lang.String FEATURE_VR_MODE = "android.software.vr.mode";
+ field public static final deprecated java.lang.String FEATURE_VR_MODE = "android.software.vr.mode";
field public static final java.lang.String FEATURE_VR_MODE_HIGH_PERFORMANCE = "android.hardware.vr.high_performance";
field public static final java.lang.String FEATURE_VULKAN_HARDWARE_COMPUTE = "android.hardware.vulkan.compute";
field public static final java.lang.String FEATURE_VULKAN_HARDWARE_LEVEL = "android.hardware.vulkan.level";
@@ -26227,9 +26230,9 @@
method public void applyTransportModeTransform(java.io.FileDescriptor, int, android.net.IpSecTransform) throws java.io.IOException;
method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
- method public void removeTransportModeTransforms(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException;
- method public void removeTransportModeTransforms(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException;
- method public void removeTransportModeTransforms(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
+ method public void removeTransportModeTransforms(java.net.Socket) throws java.io.IOException;
+ method public void removeTransportModeTransforms(java.net.DatagramSocket) throws java.io.IOException;
+ method public void removeTransportModeTransforms(java.io.FileDescriptor) throws java.io.IOException;
field public static final int DIRECTION_IN = 0; // 0x0
field public static final int DIRECTION_OUT = 1; // 0x1
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 762b6e8..ca5f66e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -430,6 +430,7 @@
method public java.lang.String getCurrentTransport();
method public boolean isAppEligibleForBackup(java.lang.String);
method public boolean isBackupEnabled();
+ method public boolean isBackupServiceActive(android.os.UserHandle);
method public java.lang.String[] listAllTransports();
method public int requestBackup(java.lang.String[], android.app.backup.BackupObserver);
method public int requestBackup(java.lang.String[], android.app.backup.BackupObserver, android.app.backup.BackupManagerMonitor, int);
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index b0c3197..dcb8eed 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -471,10 +471,13 @@
{"AID_AUTOMOTIVE_EVS", 1062},
{"AID_LOWPAN", 1063},
{"AID_HSM", 1064},
+ {"AID_RESERVED_DISK", 1065},
+ {"AID_STATSD", 1066},
+ {"AID_INCIDENTD", 1067},
{"AID_SHELL", 2000},
{"AID_CACHE", 2001},
{"AID_DIAG", 2002}};
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index aa099eb..cfe5572 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6616,7 +6616,6 @@
* to run as a {@link android.service.vr.VrListenerService} is not installed, or has
* not been enabled in user settings.
*
- * @see android.content.pm.PackageManager#FEATURE_VR_MODE
* @see android.content.pm.PackageManager#FEATURE_VR_MODE_HIGH_PERFORMANCE
* @see android.service.vr.VrListenerService
* @see android.provider.Settings#ACTION_VR_LISTENER_SETTINGS
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index e61c5b7..4bcd677 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -16,12 +16,14 @@
package android.app;
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
@@ -44,6 +46,7 @@
import android.util.Slog;
import android.view.AppTransitionAnimationSpec;
import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.RemoteAnimationAdapter;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
@@ -241,6 +244,8 @@
private static final String KEY_INSTANT_APP_VERIFICATION_BUNDLE
= "android:instantapps.installerbundle";
private static final String KEY_SPECS_FUTURE = "android:activity.specsFuture";
+ private static final String KEY_REMOTE_ANIMATION_ADAPTER
+ = "android:activity.remoteAnimationAdapter";
/** @hide */
public static final int ANIM_NONE = 0;
@@ -268,6 +273,8 @@
public static final int ANIM_CLIP_REVEAL = 11;
/** @hide */
public static final int ANIM_OPEN_CROSS_PROFILE_APPS = 12;
+ /** @hide */
+ public static final int ANIM_REMOTE_ANIMATION = 13;
private String mPackageName;
private Rect mLaunchBounds;
@@ -304,6 +311,7 @@
private int mRotationAnimationHint = -1;
private Bundle mAppVerificationBundle;
private IAppTransitionAnimationSpecsFuture mSpecsFuture;
+ private RemoteAnimationAdapter mRemoteAnimationAdapter;
/**
* Create an ActivityOptions specifying a custom animation to run when
@@ -826,6 +834,20 @@
return opts;
}
+ /**
+ * Create an {@link ActivityOptions} instance that lets the application control the entire
+ * animation using a {@link RemoteAnimationAdapter}.
+ * @hide
+ */
+ @RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS)
+ public static ActivityOptions makeRemoteAnimation(
+ RemoteAnimationAdapter remoteAnimationAdapter) {
+ final ActivityOptions opts = new ActivityOptions();
+ opts.mRemoteAnimationAdapter = remoteAnimationAdapter;
+ opts.mAnimationType = ANIM_REMOTE_ANIMATION;
+ return opts;
+ }
+
/** @hide */
public boolean getLaunchTaskBehind() {
return mAnimationType == ANIM_LAUNCH_TASK_BEHIND;
@@ -922,6 +944,7 @@
mSpecsFuture = IAppTransitionAnimationSpecsFuture.Stub.asInterface(opts.getBinder(
KEY_SPECS_FUTURE));
}
+ mRemoteAnimationAdapter = opts.getParcelable(KEY_REMOTE_ANIMATION_ADAPTER);
}
/**
@@ -1070,6 +1093,11 @@
}
/** @hide */
+ public RemoteAnimationAdapter getRemoteAnimationAdapter() {
+ return mRemoteAnimationAdapter;
+ }
+
+ /** @hide */
public static ActivityOptions fromBundle(Bundle bOptions) {
return bOptions != null ? new ActivityOptions(bOptions) : null;
}
@@ -1309,6 +1337,7 @@
mAnimSpecs = otherOptions.mAnimSpecs;
mAnimationFinishedListener = otherOptions.mAnimationFinishedListener;
mSpecsFuture = otherOptions.mSpecsFuture;
+ mRemoteAnimationAdapter = otherOptions.mRemoteAnimationAdapter;
}
/**
@@ -1403,7 +1432,9 @@
if (mAppVerificationBundle != null) {
b.putBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE, mAppVerificationBundle);
}
-
+ if (mRemoteAnimationAdapter != null) {
+ b.putParcelable(KEY_REMOTE_ANIMATION_ADAPTER, mRemoteAnimationAdapter);
+ }
return b;
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e0227c1..cc4d29e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -18,7 +18,6 @@
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
-import android.annotation.Condemned;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -50,8 +49,6 @@
import android.net.ProxyInfo;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.Process;
@@ -8983,15 +8980,6 @@
}
}
- /** {@hide} */
- @Condemned
- @Deprecated
- public boolean clearApplicationUserData(@NonNull ComponentName admin,
- @NonNull String packageName, @NonNull OnClearApplicationUserDataListener listener,
- @NonNull Handler handler) {
- return clearApplicationUserData(admin, packageName, listener, new HandlerExecutor(handler));
- }
-
/**
* Called by the device owner or profile owner to clear application user data of a given
* package. The behaviour of this is equivalent to the target application calling
@@ -9002,14 +8990,14 @@
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param packageName The name of the package which will have its user data wiped.
- * @param listener A callback object that will inform the caller when the clearing is done.
* @param executor The executor through which the listener should be invoked.
+ * @param listener A callback object that will inform the caller when the clearing is done.
* @throws SecurityException if the caller is not the device owner/profile owner.
* @return whether the clearing succeeded.
*/
public boolean clearApplicationUserData(@NonNull ComponentName admin,
- @NonNull String packageName, @NonNull OnClearApplicationUserDataListener listener,
- @NonNull @CallbackExecutor Executor executor) {
+ @NonNull String packageName, @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnClearApplicationUserDataListener listener) {
throwIfParentInstance("clearAppData");
Preconditions.checkNotNull(executor);
try {
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index b692ffd9..531bef0 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -123,4 +123,13 @@
* @param userId User ID of the profile.
*/
public abstract void reportSeparateProfileChallengeChanged(@UserIdInt int userId);
+
+ /**
+ * Check whether the user could have their password reset in an untrusted manor due to there
+ * being an admin which can call {@link #resetPassword} to reset the password without knowledge
+ * of the previous password.
+ *
+ * @param userId The user in question
+ */
+ public abstract boolean canUserHaveUntrustedCredentialReset(@UserIdInt int userId);
}
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 3a6a5b2..12f4483 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -27,6 +27,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
@@ -387,6 +388,29 @@
}
/**
+ * Report whether the backup mechanism is currently active.
+ * When it is inactive, the device will not perform any backup operations, nor will it
+ * deliver data for restore, although clients can still safely call BackupManager methods.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BACKUP)
+ public boolean isBackupServiceActive(UserHandle user) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ "isBackupServiceActive");
+ checkServiceBinder();
+ if (sService != null) {
+ try {
+ return sService.isBackupServiceActive(user.getIdentifier());
+ } catch (RemoteException e) {
+ Log.e(TAG, "isBackupEnabled() couldn't connect");
+ }
+ }
+ return false;
+ }
+
+ /**
* Enable/disable data restore at application install time. When enabled, app
* installation will include an attempt to fetch the app's historical data from
* the archival restore dataset (if any). When disabled, no such attempt will
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index b8fb2e3..27cd6e5 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -21,12 +21,10 @@
import android.annotation.StringDef;
import android.app.PendingIntent;
import android.app.RemoteInput;
-import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IContentProvider;
import android.content.Intent;
-import android.content.pm.ResolveInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
@@ -553,16 +551,11 @@
}
/**
- * Turns a slice Uri into slice content.
- *
- * @param resolver ContentResolver to be used.
- * @param uri The URI to a slice provider
- * @param supportedSpecs List of supported specs.
- * @return The Slice provided by the app or null if none is given.
- * @see Slice
+ * @deprecated TO BE REMOVED.
*/
- public static @Nullable Slice bindSlice(ContentResolver resolver, @NonNull Uri uri,
- List<SliceSpec> supportedSpecs) {
+ @Deprecated
+ public static @Nullable Slice bindSlice(ContentResolver resolver,
+ @NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
Preconditions.checkNotNull(uri, "uri");
IContentProvider provider = resolver.acquireProvider(uri);
if (provider == null) {
@@ -590,60 +583,11 @@
}
/**
- * Turns a slice intent into slice content. Expects an explicit intent. If there is no
- * {@link ContentProvider} associated with the given intent this will throw
- * {@link IllegalArgumentException}.
- *
- * @param context The context to use.
- * @param intent The intent associated with a slice.
- * @param supportedSpecs List of supported specs.
- * @return The Slice provided by the app or null if none is given.
- * @see Slice
- * @see SliceProvider#onMapIntentToUri(Intent)
- * @see Intent
+ * @deprecated TO BE REMOVED.
*/
+ @Deprecated
public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent,
- List<SliceSpec> supportedSpecs) {
- Preconditions.checkNotNull(intent, "intent");
- Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
- "Slice intent must be explicit " + intent);
- ContentResolver resolver = context.getContentResolver();
-
- // Check if the intent has data for the slice uri on it and use that
- final Uri intentData = intent.getData();
- if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
- return bindSlice(resolver, intentData, supportedSpecs);
- }
- // Otherwise ask the app
- List<ResolveInfo> providers =
- context.getPackageManager().queryIntentContentProviders(intent, 0);
- if (providers == null) {
- throw new IllegalArgumentException("Unable to resolve intent " + intent);
- }
- String authority = providers.get(0).providerInfo.authority;
- Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
- .authority(authority).build();
- IContentProvider provider = resolver.acquireProvider(uri);
- if (provider == null) {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- try {
- Bundle extras = new Bundle();
- extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
- extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
- new ArrayList<>(supportedSpecs));
- final Bundle res = provider.call(resolver.getPackageName(),
- SliceProvider.METHOD_MAP_INTENT, null, extras);
- if (res == null) {
- return null;
- }
- return res.getParcelable(SliceProvider.EXTRA_SLICE);
- } catch (RemoteException e) {
- // Arbitrary and not worth documenting, as Activity
- // Manager will kill this process shortly anyway.
- return null;
- } finally {
- resolver.releaseProvider(provider);
- }
+ @NonNull List<SliceSpec> supportedSpecs) {
+ return context.getSystemService(SliceManager.class).bindSlice(intent, supportedSpecs);
}
}
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 0c5f225d..74864cb 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -17,17 +17,29 @@
package android.app.slice;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemService;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.IContentProvider;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Pair;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
@@ -39,6 +51,8 @@
@SystemService(Context.SLICE_SERVICE)
public class SliceManager {
+ private static final String TAG = "SliceManager";
+
private final ISliceManager mService;
private final Context mContext;
private final ArrayMap<Pair<Uri, SliceCallback>, ISliceListener> mListenerLookup =
@@ -224,6 +238,126 @@
}
/**
+ * Obtains a list of slices that are descendants of the specified Uri.
+ * <p>
+ * Not all slice providers will implement this functionality, in which case,
+ * an empty collection will be returned.
+ *
+ * @param uri The uri to look for descendants under.
+ * @return All slices within the space.
+ * @see SliceProvider#onGetSliceDescendants(Uri)
+ */
+ public @NonNull Collection<Uri> getSliceDescendants(@NonNull Uri uri) {
+ ContentResolver resolver = mContext.getContentResolver();
+ IContentProvider provider = resolver.acquireProvider(uri);
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+ final Bundle res = provider.call(resolver.getPackageName(),
+ SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
+ return res.getParcelableArrayList(SliceProvider.EXTRA_SLICE_DESCENDANTS);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to get slice descendants", e);
+ } finally {
+ resolver.releaseProvider(provider);
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * Turns a slice Uri into slice content.
+ *
+ * @param uri The URI to a slice provider
+ * @param supportedSpecs List of supported specs.
+ * @return The Slice provided by the app or null if none is given.
+ * @see Slice
+ */
+ public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
+ Preconditions.checkNotNull(uri, "uri");
+ ContentResolver resolver = mContext.getContentResolver();
+ IContentProvider provider = resolver.acquireProvider(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(supportedSpecs));
+ final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE,
+ null, extras);
+ Bundle.setDefusable(res, true);
+ if (res == null) {
+ return null;
+ }
+ return res.getParcelable(SliceProvider.EXTRA_SLICE);
+ } catch (RemoteException e) {
+ // Arbitrary and not worth documenting, as Activity
+ // Manager will kill this process shortly anyway.
+ return null;
+ } finally {
+ resolver.releaseProvider(provider);
+ }
+ }
+
+ /**
+ * Turns a slice intent into slice content. Expects an explicit intent. If there is no
+ * {@link android.content.ContentProvider} associated with the given intent this will throw
+ * {@link IllegalArgumentException}.
+ *
+ * @param intent The intent associated with a slice.
+ * @param supportedSpecs List of supported specs.
+ * @return The Slice provided by the app or null if none is given.
+ * @see Slice
+ * @see SliceProvider#onMapIntentToUri(Intent)
+ * @see Intent
+ */
+ public @Nullable Slice bindSlice(@NonNull Intent intent,
+ @NonNull List<SliceSpec> supportedSpecs) {
+ Preconditions.checkNotNull(intent, "intent");
+ Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
+ "Slice intent must be explicit " + intent);
+ ContentResolver resolver = mContext.getContentResolver();
+
+ // Check if the intent has data for the slice uri on it and use that
+ final Uri intentData = intent.getData();
+ if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
+ return bindSlice(intentData, supportedSpecs);
+ }
+ // Otherwise ask the app
+ List<ResolveInfo> providers =
+ mContext.getPackageManager().queryIntentContentProviders(intent, 0);
+ if (providers == null) {
+ throw new IllegalArgumentException("Unable to resolve intent " + intent);
+ }
+ String authority = providers.get(0).providerInfo.authority;
+ Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority).build();
+ IContentProvider provider = resolver.acquireProvider(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(supportedSpecs));
+ final Bundle res = provider.call(resolver.getPackageName(),
+ SliceProvider.METHOD_MAP_INTENT, null, extras);
+ if (res == null) {
+ return null;
+ }
+ return res.getParcelable(SliceProvider.EXTRA_SLICE);
+ } catch (RemoteException e) {
+ // Arbitrary and not worth documenting, as Activity
+ // Manager will kill this process shortly anyway.
+ return null;
+ } finally {
+ resolver.releaseProvider(provider);
+ }
+ }
+
+ /**
* Class that listens to changes in {@link Slice}s.
*/
public interface SliceCallback {
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 8483931..aa41f14 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -36,6 +36,9 @@
import android.os.UserHandle;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -113,11 +116,19 @@
/**
* @hide
*/
+ public static final String METHOD_GET_DESCENDANTS = "get_descendants";
+ /**
+ * @hide
+ */
public static final String EXTRA_INTENT = "slice_intent";
/**
* @hide
*/
public static final String EXTRA_SLICE = "slice";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_SLICE_DESCENDANTS = "slice_descendants";
private static final boolean DEBUG = false;
@@ -139,14 +150,6 @@
* @see {@link Slice#HINT_PARTIAL}
*/
public Slice onBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
- return onBindSlice(sliceUri);
- }
-
- /**
- * @deprecated migrating to {@link #onBindSlice(Uri, List)}
- */
- @Deprecated
- public Slice onBindSlice(Uri sliceUri) {
return null;
}
@@ -183,6 +186,20 @@
}
/**
+ * Obtains a list of slices that are descendants of the specified Uri.
+ * <p>
+ * Implementing this is optional for a SliceProvider, but does provide a good
+ * discovery mechanism for finding slice Uris.
+ *
+ * @param uri The uri to look for descendants under.
+ * @return All slices within the space.
+ * @see SliceManager#getSliceDescendants(Uri)
+ */
+ public @NonNull Collection<Uri> onGetSliceDescendants(@NonNull Uri uri) {
+ return Collections.emptyList();
+ }
+
+ /**
* This method must be overridden if an {@link IntentFilter} is specified on the SliceProvider.
* In that case, this method can be called and is expected to return a non-null Uri representing
* a slice. Otherwise this will throw {@link UnsupportedOperationException}.
@@ -290,10 +307,35 @@
"Slice binding requires the permission BIND_SLICE");
}
handleUnpinSlice(uri);
+ } else if (method.equals(METHOD_GET_DESCENDANTS)) {
+ Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+ Bundle b = new Bundle();
+ b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS,
+ new ArrayList<>(handleGetDescendants(uri)));
+ return b;
}
return super.call(method, arg, extras);
}
+ private Collection<Uri> handleGetDescendants(Uri uri) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ return onGetSliceDescendants(uri);
+ } else {
+ CountDownLatch latch = new CountDownLatch(1);
+ Collection<Uri>[] output = new Collection[1];
+ Handler.getMain().post(() -> {
+ output[0] = onGetSliceDescendants(uri);
+ latch.countDown();
+ });
+ try {
+ latch.await();
+ return output[0];
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
private void handlePinSlice(Uri sliceUri) {
if (Looper.myLooper() == Looper.getMainLooper()) {
onSlicePinned(sliceUri);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index deb8dfb..44b4a33 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2535,31 +2535,22 @@
* Devices declaring this feature must include an application implementing a
* {@link android.service.vr.VrListenerService} that can be targeted by VR applications via
* {@link android.app.Activity#setVrModeEnabled}.
+ * @deprecated use {@link #FEATURE_VR_MODE_HIGH_PERFORMANCE} instead.
*/
+ @Deprecated
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_VR_MODE = "android.software.vr.mode";
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
- * The device implements {@link #FEATURE_VR_MODE} but additionally meets extra CDD requirements
- * to provide a high-quality VR experience. In general, devices declaring this feature will
- * additionally:
- * <ul>
- * <li>Deliver consistent performance at a high framerate over an extended period of time
- * for typical VR application CPU/GPU workloads with a minimal number of frame drops for VR
- * applications that have called
- * {@link android.view.Window#setSustainedPerformanceMode}.</li>
- * <li>Implement {@link #FEATURE_HIFI_SENSORS} and have a low sensor latency.</li>
- * <li>Include optimizations to lower display persistence while running VR applications.</li>
- * <li>Implement an optimized render path to minimize latency to draw to the device's main
- * display.</li>
- * <li>Include the following EGL extensions: EGL_ANDROID_create_native_client_buffer,
- * EGL_ANDROID_front_buffer_auto_refresh, EGL_EXT_protected_content,
- * EGL_KHR_mutable_render_buffer, EGL_KHR_reusable_sync, and EGL_KHR_wait_sync.</li>
- * <li>Provide at least one CPU core that is reserved for use solely by the top, foreground
- * VR application process for critical render threads while such an application is
- * running.</li>
- * </ul>
+ * The device implements an optimized mode for virtual reality (VR) applications that handles
+ * stereoscopic rendering of notifications, disables most monocular system UI components
+ * while a VR application has user focus and meets extra CDD requirements to provide a
+ * high-quality VR experience.
+ * Devices declaring this feature must include an application implementing a
+ * {@link android.service.vr.VrListenerService} that can be targeted by VR applications via
+ * {@link android.app.Activity#setVrModeEnabled}.
+ * and must meet CDD requirements to provide a high-quality VR experience.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_VR_MODE_HIGH_PERFORMANCE
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 3f6dd2e..078958a 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -179,6 +179,11 @@
public abstract void persistBrightnessSliderEvents();
/**
+ * Notifies the display manager that resource overlays have changed.
+ */
+ public abstract void onOverlayChanged();
+
+ /**
* Describes the requested power state of the display.
*
* This object is intended to describe the general characteristics of the
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index 3fe531f..790c80b 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -45,5 +45,5 @@
void applyTransportModeTransform(in ParcelFileDescriptor socket, int direction, int transformId);
- void removeTransportModeTransforms(in ParcelFileDescriptor socket, int transformId);
+ void removeTransportModeTransforms(in ParcelFileDescriptor socket);
}
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 2202df3..2cda58c 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -405,62 +405,56 @@
/**
* Remove an IPsec transform from a stream socket.
*
- * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed
- * regardless of the state of the transform. Removing a transform from a socket allows the
- * socket to be reused for communication in the clear.
+ * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
+ * socket allows the socket to be reused for communication in the clear.
*
* <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
* {@link IpSecTransform#close()}, then communication on the socket will fail until this method
* is called.
*
* @param socket a socket that previously had a transform applied to it
- * @param transform the IPsec Transform that was previously applied to the given socket
* @throws IOException indicating that the transform could not be removed from the socket
*/
- public void removeTransportModeTransforms(Socket socket, IpSecTransform transform)
+ public void removeTransportModeTransforms(Socket socket)
throws IOException {
- removeTransportModeTransforms(socket.getFileDescriptor$(), transform);
+ removeTransportModeTransforms(socket.getFileDescriptor$());
}
/**
* Remove an IPsec transform from a datagram socket.
*
- * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed
- * regardless of the state of the transform. Removing a transform from a socket allows the
- * socket to be reused for communication in the clear.
+ * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
+ * socket allows the socket to be reused for communication in the clear.
*
* <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
* {@link IpSecTransform#close()}, then communication on the socket will fail until this method
* is called.
*
* @param socket a socket that previously had a transform applied to it
- * @param transform the IPsec Transform that was previously applied to the given socket
* @throws IOException indicating that the transform could not be removed from the socket
*/
- public void removeTransportModeTransforms(DatagramSocket socket, IpSecTransform transform)
+ public void removeTransportModeTransforms(DatagramSocket socket)
throws IOException {
- removeTransportModeTransforms(socket.getFileDescriptor$(), transform);
+ removeTransportModeTransforms(socket.getFileDescriptor$());
}
/**
* Remove an IPsec transform from a socket.
*
- * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed
- * regardless of the state of the transform. Removing a transform from a socket allows the
- * socket to be reused for communication in the clear.
+ * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
+ * socket allows the socket to be reused for communication in the clear.
*
* <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
* {@link IpSecTransform#close()}, then communication on the socket will fail until this method
* is called.
*
* @param socket a socket that previously had a transform applied to it
- * @param transform the IPsec Transform that was previously applied to the given socket
* @throws IOException indicating that the transform could not be removed from the socket
*/
- public void removeTransportModeTransforms(FileDescriptor socket, IpSecTransform transform)
+ public void removeTransportModeTransforms(FileDescriptor socket)
throws IOException {
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
- mService.removeTransportModeTransforms(pfd, transform.getResourceId());
+ mService.removeTransportModeTransforms(pfd);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6d91f59..456ea6a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9852,6 +9852,15 @@
public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled";
/**
+ * Whether or not to enable Forced App Standby on small battery devices.
+ * Type: int (0 for false, 1 for true)
+ * Default: 0
+ * @hide
+ */
+ public static final String FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED
+ = "forced_app_standby_for_small_battery_enabled";
+
+ /**
* Whether or not Network Watchlist feature is enabled.
* Type: int (0 for false, 1 for true)
* Default: 0
diff --git a/core/java/android/security/keystore/EntryRecoveryData.aidl b/core/java/android/security/keystore/KeychainProtectionParameter.aidl
similarity index 93%
copy from core/java/android/security/keystore/EntryRecoveryData.aidl
copy to core/java/android/security/keystore/KeychainProtectionParameter.aidl
index c6c20e3..1e2c365 100644
--- a/core/java/android/security/keystore/EntryRecoveryData.aidl
+++ b/core/java/android/security/keystore/KeychainProtectionParameter.aidl
@@ -17,4 +17,4 @@
package android.security.keystore;
/* @hide */
-parcelable EntryRecoveryData;
+parcelable KeychainProtectionParameter;
diff --git a/core/java/android/security/keystore/RecoveryMetadata.java b/core/java/android/security/keystore/KeychainProtectionParameter.java
similarity index 78%
rename from core/java/android/security/keystore/RecoveryMetadata.java
rename to core/java/android/security/keystore/KeychainProtectionParameter.java
index 3f09455..2319ef5 100644
--- a/core/java/android/security/keystore/RecoveryMetadata.java
+++ b/core/java/android/security/keystore/KeychainProtectionParameter.java
@@ -28,12 +28,26 @@
import java.util.Arrays;
/**
- * Helper class with data necessary to recover Keystore on a new device.
- * It defines UI shown to the user and a way to derive a cryptographic key from user output.
+ * A {@link KeychainSnapshot} is protected with a key derived from the user's lock screen. This
+ * class wraps all the data necessary to derive the same key on a recovering device:
+ *
+ * <ul>
+ * <li>UI parameters for the user's lock screen - so that if e.g., the user was using a pattern,
+ * the recovering device can display the pattern UI to the user when asking them to enter
+ * the lock screen from their previous device.
+ * <li>The algorithm used to derive a key from the user's lock screen, e.g. SHA-256 with a salt.
+ * </ul>
+ *
+ * <p>As such, this data is sent along with the {@link KeychainSnapshot} when syncing the current
+ * version of the keychain.
+ *
+ * <p>For now, the recoverable keychain only supports a single layer of protection, which is the
+ * user's lock screen. In the future, the keychain will support multiple layers of protection
+ * (e.g. an additional keychain password, along with the lock screen).
*
* @hide
*/
-public final class RecoveryMetadata implements Parcelable {
+public final class KeychainProtectionParameter implements Parcelable {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({TYPE_LOCKSCREEN, TYPE_CUSTOM_PASSWORD})
@@ -88,7 +102,7 @@
* @link {#clearSecret} to overwrite its value in memory.
* @hide
*/
- public RecoveryMetadata(@UserSecretType int userSecretType,
+ public KeychainProtectionParameter(@UserSecretType int userSecretType,
@LockScreenUiFormat int lockScreenUiFormat,
@NonNull KeyDerivationParams keyDerivationParams,
@NonNull byte[] secret) {
@@ -98,7 +112,7 @@
mSecret = Preconditions.checkNotNull(secret);
}
- private RecoveryMetadata() {
+ private KeychainProtectionParameter() {
}
@@ -141,10 +155,10 @@
}
/**
- * Builder for creating {@link RecoveryMetadata}.
+ * Builder for creating {@link KeychainProtectionParameter}.
*/
public static class Builder {
- private RecoveryMetadata mInstance = new RecoveryMetadata();
+ private KeychainProtectionParameter mInstance = new KeychainProtectionParameter();
/**
* Sets user secret type.
@@ -198,14 +212,14 @@
/**
- * Creates a new {@link RecoveryMetadata} instance.
+ * Creates a new {@link KeychainProtectionParameter} instance.
* The instance will include default values, if {@link setSecret}
* or {@link setUserSecretType} were not called.
*
* @return new instance
* @throws NullPointerException if some required fields were not set.
*/
- public @NonNull RecoveryMetadata build() {
+ @NonNull public KeychainProtectionParameter build() {
if (mInstance.mUserSecretType == null) {
mInstance.mUserSecretType = TYPE_LOCKSCREEN;
}
@@ -235,14 +249,14 @@
Arrays.fill(mSecret, (byte) 0);
}
- public static final Parcelable.Creator<RecoveryMetadata> CREATOR =
- new Parcelable.Creator<RecoveryMetadata>() {
- public RecoveryMetadata createFromParcel(Parcel in) {
- return new RecoveryMetadata(in);
+ public static final Parcelable.Creator<KeychainProtectionParameter> CREATOR =
+ new Parcelable.Creator<KeychainProtectionParameter>() {
+ public KeychainProtectionParameter createFromParcel(Parcel in) {
+ return new KeychainProtectionParameter(in);
}
- public RecoveryMetadata[] newArray(int length) {
- return new RecoveryMetadata[length];
+ public KeychainProtectionParameter[] newArray(int length) {
+ return new KeychainProtectionParameter[length];
}
};
@@ -260,7 +274,7 @@
/**
* @hide
*/
- protected RecoveryMetadata(Parcel in) {
+ protected KeychainProtectionParameter(Parcel in) {
mUserSecretType = in.readInt();
mLockScreenUiFormat = in.readInt();
mKeyDerivationParams = in.readTypedObject(KeyDerivationParams.CREATOR);
diff --git a/core/java/android/security/keystore/EntryRecoveryData.aidl b/core/java/android/security/keystore/KeychainSnapshot.aidl
similarity index 95%
rename from core/java/android/security/keystore/EntryRecoveryData.aidl
rename to core/java/android/security/keystore/KeychainSnapshot.aidl
index c6c20e3..b35713f 100644
--- a/core/java/android/security/keystore/EntryRecoveryData.aidl
+++ b/core/java/android/security/keystore/KeychainSnapshot.aidl
@@ -17,4 +17,4 @@
package android.security.keystore;
/* @hide */
-parcelable EntryRecoveryData;
+parcelable KeychainSnapshot;
diff --git a/core/java/android/security/keystore/KeychainSnapshot.java b/core/java/android/security/keystore/KeychainSnapshot.java
new file mode 100644
index 0000000..71a808a
--- /dev/null
+++ b/core/java/android/security/keystore/KeychainSnapshot.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * A snapshot of a version of the keystore. Two events can trigger the generation of a new snapshot:
+ *
+ * <ul>
+ * <li>The user's lock screen changes. (A key derived from the user's lock screen is used to
+ * protected the keychain, which is why this forces a new snapshot.)
+ * <li>A key is added to or removed from the recoverable keychain.
+ * </ul>
+ *
+ * <p>The snapshot data is also encrypted with the remote trusted hardware's public key, so even
+ * the recovery agent itself should not be able to decipher the data. The recovery agent sends an
+ * instance of this to the remote trusted hardware whenever a new snapshot is generated. During a
+ * recovery flow, the recovery agent retrieves a snapshot from the remote trusted hardware. It then
+ * sends it to the framework, where it is decrypted using the user's lock screen from their previous
+ * device.
+ *
+ * @hide
+ */
+public final class KeychainSnapshot implements Parcelable {
+ private int mSnapshotVersion;
+ private List<KeychainProtectionParameter> mKeychainProtectionParams;
+ private List<WrappedApplicationKey> mEntryRecoveryData;
+ private byte[] mEncryptedRecoveryKeyBlob;
+
+ /**
+ * @hide
+ * Deprecated, consider using builder.
+ */
+ public KeychainSnapshot(
+ int snapshotVersion,
+ @NonNull List<KeychainProtectionParameter> keychainProtectionParams,
+ @NonNull List<WrappedApplicationKey> wrappedApplicationKeys,
+ @NonNull byte[] encryptedRecoveryKeyBlob) {
+ mSnapshotVersion = snapshotVersion;
+ mKeychainProtectionParams =
+ Preconditions.checkCollectionElementsNotNull(keychainProtectionParams,
+ "keychainProtectionParams");
+ mEntryRecoveryData = Preconditions.checkCollectionElementsNotNull(wrappedApplicationKeys,
+ "wrappedApplicationKeys");
+ mEncryptedRecoveryKeyBlob = Preconditions.checkNotNull(encryptedRecoveryKeyBlob);
+ }
+
+ private KeychainSnapshot() {
+
+ }
+
+ /**
+ * Snapshot version for given account. It is incremented when user secret or list of application
+ * keys changes.
+ */
+ public int getSnapshotVersion() {
+ return mSnapshotVersion;
+ }
+
+ /**
+ * UI and key derivation parameters. Note that combination of secrets may be used.
+ */
+ public @NonNull List<KeychainProtectionParameter> getKeychainProtectionParams() {
+ return mKeychainProtectionParams;
+ }
+
+ /**
+ * List of application keys, with key material encrypted by
+ * the recovery key ({@link #getEncryptedRecoveryKeyBlob}).
+ */
+ public @NonNull List<WrappedApplicationKey> getWrappedApplicationKeys() {
+ return mEntryRecoveryData;
+ }
+
+ /**
+ * Recovery key blob, encrypted by user secret and recovery service public key.
+ */
+ public @NonNull byte[] getEncryptedRecoveryKeyBlob() {
+ return mEncryptedRecoveryKeyBlob;
+ }
+
+ public static final Parcelable.Creator<KeychainSnapshot> CREATOR =
+ new Parcelable.Creator<KeychainSnapshot>() {
+ public KeychainSnapshot createFromParcel(Parcel in) {
+ return new KeychainSnapshot(in);
+ }
+
+ public KeychainSnapshot[] newArray(int length) {
+ return new KeychainSnapshot[length];
+ }
+ };
+
+ /**
+ * Builder for creating {@link KeychainSnapshot}.
+ */
+ public static class Builder {
+ private KeychainSnapshot mInstance = new KeychainSnapshot();
+
+ /**
+ * Snapshot version for given account.
+ *
+ * @param snapshotVersion The snapshot version
+ * @return This builder.
+ */
+ public Builder setSnapshotVersion(int snapshotVersion) {
+ mInstance.mSnapshotVersion = snapshotVersion;
+ return this;
+ }
+
+ /**
+ * Sets UI and key derivation parameters
+ *
+ * @param recoveryMetadata The UI and key derivation parameters
+ * @return This builder.
+ */
+ public Builder setKeychainProtectionParams(
+ @NonNull List<KeychainProtectionParameter> recoveryMetadata) {
+ mInstance.mKeychainProtectionParams = recoveryMetadata;
+ return this;
+ }
+
+ /**
+ * List of application keys.
+ *
+ * @param entryRecoveryData List of application keys
+ * @return This builder.
+ */
+ public Builder setWrappedApplicationKeys(List<WrappedApplicationKey> entryRecoveryData) {
+ mInstance.mEntryRecoveryData = entryRecoveryData;
+ return this;
+ }
+
+ /**
+ * Sets recovery key blob
+ *
+ * @param encryptedRecoveryKeyBlob The recovery key blob.
+ * @return This builder.
+ */
+ public Builder setEncryptedRecoveryKeyBlob(@NonNull byte[] encryptedRecoveryKeyBlob) {
+ mInstance.mEncryptedRecoveryKeyBlob = encryptedRecoveryKeyBlob;
+ return this;
+ }
+
+
+ /**
+ * Creates a new {@link KeychainSnapshot} instance.
+ *
+ * @return new instance
+ * @throws NullPointerException if some required fields were not set.
+ */
+ @NonNull public KeychainSnapshot build() {
+ Preconditions.checkCollectionElementsNotNull(mInstance.mKeychainProtectionParams,
+ "recoveryMetadata");
+ Preconditions.checkCollectionElementsNotNull(mInstance.mEntryRecoveryData,
+ "entryRecoveryData");
+ Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob);
+ return mInstance;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mSnapshotVersion);
+ out.writeTypedList(mKeychainProtectionParams);
+ out.writeByteArray(mEncryptedRecoveryKeyBlob);
+ out.writeTypedList(mEntryRecoveryData);
+ }
+
+ /**
+ * @hide
+ */
+ protected KeychainSnapshot(Parcel in) {
+ mSnapshotVersion = in.readInt();
+ mKeychainProtectionParams = in.createTypedArrayList(KeychainProtectionParameter.CREATOR);
+ mEncryptedRecoveryKeyBlob = in.createByteArray();
+ mEntryRecoveryData = in.createTypedArrayList(WrappedApplicationKey.CREATOR);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/security/keystore/RecoveryData.java b/core/java/android/security/keystore/RecoveryData.java
deleted file mode 100644
index 897aa18..0000000
--- a/core/java/android/security/keystore/RecoveryData.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.List;
-
-/**
- * Helper class which returns data necessary to recover keys.
- * Contains
- *
- * <ul>
- * <li>Snapshot version.
- * <li>Recovery metadata with UI and key derivation parameters.
- * <li>List of application keys encrypted by recovery key.
- * <li>Encrypted recovery key.
- * </ul>
- *
- * @hide
- */
-public final class RecoveryData implements Parcelable {
- private int mSnapshotVersion;
- private List<RecoveryMetadata> mRecoveryMetadata;
- private List<EntryRecoveryData> mEntryRecoveryData;
- private byte[] mEncryptedRecoveryKeyBlob;
-
- /**
- * @hide
- * Deprecated, consider using builder.
- */
- public RecoveryData(
- int snapshotVersion,
- @NonNull List<RecoveryMetadata> recoveryMetadata,
- @NonNull List<EntryRecoveryData> entryRecoveryData,
- @NonNull byte[] encryptedRecoveryKeyBlob) {
- mSnapshotVersion = snapshotVersion;
- mRecoveryMetadata =
- Preconditions.checkCollectionElementsNotNull(recoveryMetadata, "recoveryMetadata");
- mEntryRecoveryData = Preconditions.checkCollectionElementsNotNull(entryRecoveryData,
- "entryRecoveryData");
- mEncryptedRecoveryKeyBlob = Preconditions.checkNotNull(encryptedRecoveryKeyBlob);
- }
-
- private RecoveryData() {
-
- }
-
- /**
- * Snapshot version for given account. It is incremented when user secret or list of application
- * keys changes.
- */
- public int getSnapshotVersion() {
- return mSnapshotVersion;
- }
-
- /**
- * UI and key derivation parameters. Note that combination of secrets may be used.
- */
- public @NonNull List<RecoveryMetadata> getRecoveryMetadata() {
- return mRecoveryMetadata;
- }
-
- /**
- * List of application keys, with key material encrypted by
- * the recovery key ({@link #getEncryptedRecoveryKeyBlob}).
- */
- public @NonNull List<EntryRecoveryData> getEntryRecoveryData() {
- return mEntryRecoveryData;
- }
-
- /**
- * Recovery key blob, encrypted by user secret and recovery service public key.
- */
- public @NonNull byte[] getEncryptedRecoveryKeyBlob() {
- return mEncryptedRecoveryKeyBlob;
- }
-
- public static final Parcelable.Creator<RecoveryData> CREATOR =
- new Parcelable.Creator<RecoveryData>() {
- public RecoveryData createFromParcel(Parcel in) {
- return new RecoveryData(in);
- }
-
- public RecoveryData[] newArray(int length) {
- return new RecoveryData[length];
- }
- };
-
- /**
- * Builder for creating {@link RecoveryData}.
- */
- public static class Builder {
- private RecoveryData mInstance = new RecoveryData();
-
- /**
- * Snapshot version for given account.
- *
- * @param snapshotVersion The snapshot version
- * @return This builder.
- */
- public Builder setSnapshotVersion(int snapshotVersion) {
- mInstance.mSnapshotVersion = snapshotVersion;
- return this;
- }
-
- /**
- * Sets UI and key derivation parameters
- *
- * @param recoveryMetadata The UI and key derivation parameters
- * @return This builder.
- */
- public Builder setRecoveryMetadata(@NonNull List<RecoveryMetadata> recoveryMetadata) {
- mInstance.mRecoveryMetadata = recoveryMetadata;
- return this;
- }
-
- /**
- * List of application keys.
- *
- * @param entryRecoveryData List of application keys
- * @return This builder.
- */
- public Builder setEntryRecoveryData(List<EntryRecoveryData> entryRecoveryData) {
- mInstance.mEntryRecoveryData = entryRecoveryData;
- return this;
- }
-
- /**
- * Sets recovery key blob
- *
- * @param encryptedRecoveryKeyBlob The recovery key blob.
- * @return This builder.
- */
- public Builder setEncryptedRecoveryKeyBlob(@NonNull byte[] encryptedRecoveryKeyBlob) {
- mInstance.mEncryptedRecoveryKeyBlob = encryptedRecoveryKeyBlob;
- return this;
- }
-
-
- /**
- * Creates a new {@link RecoveryData} instance.
- *
- * @return new instance
- * @throws NullPointerException if some required fields were not set.
- */
- public @NonNull RecoveryData build() {
- Preconditions.checkCollectionElementsNotNull(mInstance.mRecoveryMetadata,
- "recoveryMetadata");
- Preconditions.checkCollectionElementsNotNull(mInstance.mEntryRecoveryData,
- "entryRecoveryData");
- Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob);
- return mInstance;
- }
- }
-
- /**
- * @hide
- */
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(mSnapshotVersion);
- out.writeTypedList(mRecoveryMetadata);
- out.writeByteArray(mEncryptedRecoveryKeyBlob);
- out.writeTypedList(mEntryRecoveryData);
- }
-
- /**
- * @hide
- */
- protected RecoveryData(Parcel in) {
- mSnapshotVersion = in.readInt();
- mRecoveryMetadata = in.createTypedArrayList(RecoveryMetadata.CREATOR);
- mEncryptedRecoveryKeyBlob = in.createByteArray();
- mEntryRecoveryData = in.createTypedArrayList(EntryRecoveryData.CREATOR);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-}
diff --git a/core/java/android/security/keystore/RecoveryManager.java b/core/java/android/security/keystore/RecoveryManager.java
index 99bd284..bddf3e8 100644
--- a/core/java/android/security/keystore/RecoveryManager.java
+++ b/core/java/android/security/keystore/RecoveryManager.java
@@ -99,11 +99,11 @@
* @return Data necessary to recover keystore.
* @hide
*/
- public @NonNull RecoveryData getRecoveryData(@NonNull byte[] account)
+ @NonNull public KeychainSnapshot getRecoveryData(@NonNull byte[] account)
throws RecoveryManagerException {
try {
- RecoveryData recoveryData = mBinder.getRecoveryData(account);
- return recoveryData;
+ KeychainSnapshot keychainSnapshot = mBinder.getRecoveryData(account);
+ return keychainSnapshot;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
@@ -136,7 +136,7 @@
* version. Version zero is used, if no snapshots were created for the account.
*
* @return Map from recovery agent accounts to snapshot versions.
- * @see RecoveryData#getSnapshotVersion
+ * @see KeychainSnapshot#getSnapshotVersion
* @hide
*/
public @NonNull Map<byte[], Integer> getRecoverySnapshotVersions()
@@ -156,7 +156,7 @@
/**
* Server parameters used to generate new recovery key blobs. This value will be included in
- * {@code RecoveryData.getEncryptedRecoveryKeyBlob()}. The same value must be included
+ * {@code KeychainSnapshot.getEncryptedRecoveryKeyBlob()}. The same value must be included
* in vaultParams {@link #startRecoverySession}
*
* @param serverParams included in recovery key blob.
@@ -230,11 +230,11 @@
* Specifies a set of secret types used for end-to-end keystore encryption. Knowing all of them
* is necessary to recover data.
*
- * @param secretTypes {@link RecoveryMetadata#TYPE_LOCKSCREEN} or {@link
- * RecoveryMetadata#TYPE_CUSTOM_PASSWORD}
+ * @param secretTypes {@link KeychainProtectionParameter#TYPE_LOCKSCREEN} or {@link
+ * KeychainProtectionParameter#TYPE_CUSTOM_PASSWORD}
*/
public void setRecoverySecretTypes(
- @NonNull @RecoveryMetadata.UserSecretType int[] secretTypes)
+ @NonNull @KeychainProtectionParameter.UserSecretType int[] secretTypes)
throws RecoveryManagerException {
try {
mBinder.setRecoverySecretTypes(secretTypes);
@@ -247,12 +247,12 @@
/**
* Defines a set of secret types used for end-to-end keystore encryption. Knowing all of them is
- * necessary to generate RecoveryData.
+ * necessary to generate KeychainSnapshot.
*
* @return list of recovery secret types
- * @see RecoveryData
+ * @see KeychainSnapshot
*/
- public @NonNull @RecoveryMetadata.UserSecretType int[] getRecoverySecretTypes()
+ @NonNull public @KeychainProtectionParameter.UserSecretType int[] getRecoverySecretTypes()
throws RecoveryManagerException {
try {
return mBinder.getRecoverySecretTypes();
@@ -271,7 +271,8 @@
* @return list of recovery secret types
* @hide
*/
- public @NonNull @RecoveryMetadata.UserSecretType int[] getPendingRecoverySecretTypes()
+ @NonNull
+ public @KeychainProtectionParameter.UserSecretType int[] getPendingRecoverySecretTypes()
throws RecoveryManagerException {
try {
return mBinder.getPendingRecoverySecretTypes();
@@ -285,14 +286,14 @@
/**
* Method notifies KeyStore that a user-generated secret is available. This method generates a
* symmetric session key which a trusted remote device can use to return a recovery key. Caller
- * should use {@link RecoveryMetadata#clearSecret} to override the secret value in
+ * should use {@link KeychainProtectionParameter#clearSecret} to override the secret value in
* memory.
*
* @param recoverySecret user generated secret together with parameters necessary to regenerate
* it on a new device.
* @hide
*/
- public void recoverySecretAvailable(@NonNull RecoveryMetadata recoverySecret)
+ public void recoverySecretAvailable(@NonNull KeychainProtectionParameter recoverySecret)
throws RecoveryManagerException {
try {
mBinder.recoverySecretAvailable(recoverySecret);
@@ -326,7 +327,7 @@
@NonNull byte[] verifierPublicKey,
@NonNull byte[] vaultParams,
@NonNull byte[] vaultChallenge,
- @NonNull List<RecoveryMetadata> secrets)
+ @NonNull List<KeychainProtectionParameter> secrets)
throws RecoveryManagerException {
try {
byte[] recoveryClaim =
@@ -352,13 +353,13 @@
* @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
* @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
* and session. KeyStore only uses package names from the application info in {@link
- * EntryRecoveryData}. Caller is responsibility to perform certificates check.
+ * WrappedApplicationKey}. Caller is responsibility to perform certificates check.
* @return Map from alias to raw key material.
*/
public Map<String, byte[]> recoverKeys(
@NonNull String sessionId,
@NonNull byte[] recoveryKeyBlob,
- @NonNull List<EntryRecoveryData> applicationKeys)
+ @NonNull List<WrappedApplicationKey> applicationKeys)
throws RecoveryManagerException {
try {
return (Map<String, byte[]>) mBinder.recoverKeys(
diff --git a/core/java/android/security/keystore/RecoveryMetadata.aidl b/core/java/android/security/keystore/RecoveryMetadata.aidl
deleted file mode 100644
index 8e342b4..0000000
--- a/core/java/android/security/keystore/RecoveryMetadata.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-/* @hide */
-parcelable RecoveryMetadata;
diff --git a/core/java/android/security/keystore/EntryRecoveryData.aidl b/core/java/android/security/keystore/WrappedApplicationKey.aidl
similarity index 94%
copy from core/java/android/security/keystore/EntryRecoveryData.aidl
copy to core/java/android/security/keystore/WrappedApplicationKey.aidl
index c6c20e3..a6294fe 100644
--- a/core/java/android/security/keystore/EntryRecoveryData.aidl
+++ b/core/java/android/security/keystore/WrappedApplicationKey.aidl
@@ -17,4 +17,4 @@
package android.security.keystore;
/* @hide */
-parcelable EntryRecoveryData;
+parcelable WrappedApplicationKey;
diff --git a/core/java/android/security/keystore/EntryRecoveryData.java b/core/java/android/security/keystore/WrappedApplicationKey.java
similarity index 78%
rename from core/java/android/security/keystore/EntryRecoveryData.java
rename to core/java/android/security/keystore/WrappedApplicationKey.java
index aaca3fe..522bb95 100644
--- a/core/java/android/security/keystore/EntryRecoveryData.java
+++ b/core/java/android/security/keystore/WrappedApplicationKey.java
@@ -35,16 +35,16 @@
*
* @hide
*/
-public final class EntryRecoveryData implements Parcelable {
+public final class WrappedApplicationKey implements Parcelable {
private String mAlias;
// The only supported format is AES-256 symmetric key.
private byte[] mEncryptedKeyMaterial;
/**
- * Builder for creating {@link EntryRecoveryData}.
+ * Builder for creating {@link WrappedApplicationKey}.
*/
public static class Builder {
- private EntryRecoveryData mInstance = new EntryRecoveryData();
+ private WrappedApplicationKey mInstance = new WrappedApplicationKey();
/**
* Sets Application-specific alias of the key.
@@ -70,19 +70,19 @@
}
/**
- * Creates a new {@link EntryRecoveryData} instance.
+ * Creates a new {@link WrappedApplicationKey} instance.
*
* @return new instance
* @throws NullPointerException if some required fields were not set.
*/
- public @NonNull EntryRecoveryData build() {
+ @NonNull public WrappedApplicationKey build() {
Preconditions.checkNotNull(mInstance.mAlias);
Preconditions.checkNotNull(mInstance.mEncryptedKeyMaterial);
return mInstance;
}
}
- private EntryRecoveryData() {
+ private WrappedApplicationKey() {
}
@@ -90,7 +90,7 @@
* Deprecated - consider using Builder.
* @hide
*/
- public EntryRecoveryData(@NonNull String alias, @NonNull byte[] encryptedKeyMaterial) {
+ public WrappedApplicationKey(@NonNull String alias, @NonNull byte[] encryptedKeyMaterial) {
mAlias = Preconditions.checkNotNull(alias);
mEncryptedKeyMaterial = Preconditions.checkNotNull(encryptedKeyMaterial);
}
@@ -109,14 +109,14 @@
return mEncryptedKeyMaterial;
}
- public static final Parcelable.Creator<EntryRecoveryData> CREATOR =
- new Parcelable.Creator<EntryRecoveryData>() {
- public EntryRecoveryData createFromParcel(Parcel in) {
- return new EntryRecoveryData(in);
+ public static final Parcelable.Creator<WrappedApplicationKey> CREATOR =
+ new Parcelable.Creator<WrappedApplicationKey>() {
+ public WrappedApplicationKey createFromParcel(Parcel in) {
+ return new WrappedApplicationKey(in);
}
- public EntryRecoveryData[] newArray(int length) {
- return new EntryRecoveryData[length];
+ public WrappedApplicationKey[] newArray(int length) {
+ return new WrappedApplicationKey[length];
}
};
@@ -132,7 +132,7 @@
/**
* @hide
*/
- protected EntryRecoveryData(Parcel in) {
+ protected WrappedApplicationKey(Parcel in) {
mAlias = in.readString();
mEncryptedKeyMaterial = in.createByteArray();
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 20cd906..b7b2b2d 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -55,6 +55,7 @@
import android.widget.RemoteViews;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import java.lang.annotation.Retention;
@@ -1543,7 +1544,11 @@
return mShowBadge;
}
- private void populate(String key, int rank, boolean matchesInterruptionFilter,
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public void populate(String key, int rank, boolean matchesInterruptionFilter,
int visibilityOverride, int suppressedVisualEffects, int importance,
CharSequence explanation, String overrideGroupKey,
NotificationChannel channel, ArrayList<String> overridePeople,
diff --git a/core/java/android/security/keystore/EntryRecoveryData.aidl b/core/java/android/view/IRemoteAnimationFinishedCallback.aidl
similarity index 61%
copy from core/java/android/security/keystore/EntryRecoveryData.aidl
copy to core/java/android/view/IRemoteAnimationFinishedCallback.aidl
index c6c20e3..ae58b22 100644
--- a/core/java/android/security/keystore/EntryRecoveryData.aidl
+++ b/core/java/android/view/IRemoteAnimationFinishedCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,10 +11,17 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package android.security.keystore;
+package android.view;
-/* @hide */
-parcelable EntryRecoveryData;
+/**
+ * Interface to be invoked by the controlling process when a remote animation has finished.
+ *
+ * @see IRemoteAnimationRunner
+ * {@hide}
+ */
+interface IRemoteAnimationFinishedCallback {
+ void onAnimationFinished();
+}
diff --git a/core/java/android/view/IRemoteAnimationRunner.aidl b/core/java/android/view/IRemoteAnimationRunner.aidl
new file mode 100644
index 0000000..1350ebf
--- /dev/null
+++ b/core/java/android/view/IRemoteAnimationRunner.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.view.RemoteAnimationTarget;
+import android.view.IRemoteAnimationFinishedCallback;
+
+/**
+ * Interface that is used to callback from window manager to the process that runs a remote
+ * animation to start or cancel it.
+ *
+ * {@hide}
+ */
+oneway interface IRemoteAnimationRunner {
+
+ /**
+ * Called when the process needs to start the remote animation.
+ *
+ * @param apps The list of apps to animate.
+ * @param finishedCallback The callback to invoke when the animation is finished.
+ */
+ void onAnimationStart(in RemoteAnimationTarget[] apps,
+ in IRemoteAnimationFinishedCallback finishedCallback);
+
+ /**
+ * Called when the animation was cancelled. From this point on, any updates onto the leashes
+ * won't have any effect anymore.
+ */
+ void onAnimationCancelled();
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index bfcf285..4adcb8f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -38,6 +38,7 @@
import android.view.IDockedStackListener;
import android.view.IOnKeyguardExitResult;
import android.view.IPinnedStackListener;
+import android.view.RemoteAnimationAdapter;
import android.view.IRotationWatcher;
import android.view.IWallpaperVisibilityListener;
import android.view.IWindowSession;
@@ -124,6 +125,7 @@
void overridePendingAppTransitionMultiThumbFuture(
IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback startedCallback,
boolean scaleUp);
+ void overridePendingAppTransitionRemote(in RemoteAnimationAdapter remoteAnimationAdapter);
void executeAppTransition();
/** Used by system ui to report that recents has shown itself. */
diff --git a/core/java/android/security/keystore/RecoveryData.aidl b/core/java/android/view/RemoteAnimationAdapter.aidl
similarity index 76%
copy from core/java/android/security/keystore/RecoveryData.aidl
copy to core/java/android/view/RemoteAnimationAdapter.aidl
index 4200de1..855bc74 100644
--- a/core/java/android/security/keystore/RecoveryData.aidl
+++ b/core/java/android/view/RemoteAnimationAdapter.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,10 +11,9 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package android.security.keystore;
+package android.view;
-/* @hide */
-parcelable RecoveryData;
+parcelable RemoteAnimationAdapter;
diff --git a/core/java/android/view/RemoteAnimationAdapter.java b/core/java/android/view/RemoteAnimationAdapter.java
new file mode 100644
index 0000000..d597e59
--- /dev/null
+++ b/core/java/android/view/RemoteAnimationAdapter.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import android.app.ActivityOptions;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Object that describes how to run a remote animation.
+ * <p>
+ * A remote animation lets another app control the entire app transition. It does so by
+ * <ul>
+ * <li>using {@link ActivityOptions#makeRemoteAnimation}</li>
+ * <li>using {@link IWindowManager#overridePendingAppTransitionRemote}</li>
+ * </ul>
+ * to register a {@link RemoteAnimationAdapter} that describes how the animation should be run:
+ * Along some meta-data, this object contains a callback that gets invoked from window manager when
+ * the transition is ready to be started.
+ * <p>
+ * Window manager supplies a list of {@link RemoteAnimationTarget}s into the callback. Each target
+ * contains information about the activity that is animating as well as
+ * {@link RemoteAnimationTarget#leash}. The controlling app can modify the leash like any other
+ * {@link SurfaceControl}, including the possibility to synchronize updating the leash's surface
+ * properties with a frame to be drawn using
+ * {@link SurfaceControl.Transaction#deferTransactionUntil}.
+ * <p>
+ * When the animation is done, the controlling app can invoke
+ * {@link IRemoteAnimationFinishedCallback} that gets supplied into
+ * {@link IRemoteAnimationRunner#onStartAnimation}
+ *
+ * @hide
+ */
+public class RemoteAnimationAdapter implements Parcelable {
+
+ private final IRemoteAnimationRunner mRunner;
+ private final long mDuration;
+ private final long mStatusBarTransitionDelay;
+
+ /**
+ * @param runner The interface that gets notified when we actually need to start the animation.
+ * @param duration The duration of the animation.
+ * @param statusBarTransitionDelay The desired delay for all visual animations in the
+ * status bar caused by this app animation in millis.
+ */
+ public RemoteAnimationAdapter(IRemoteAnimationRunner runner, long duration,
+ long statusBarTransitionDelay) {
+ mRunner = runner;
+ mDuration = duration;
+ mStatusBarTransitionDelay = statusBarTransitionDelay;
+ }
+
+ public RemoteAnimationAdapter(Parcel in) {
+ mRunner = IRemoteAnimationRunner.Stub.asInterface(in.readStrongBinder());
+ mDuration = in.readLong();
+ mStatusBarTransitionDelay = in.readLong();
+ }
+
+ public IRemoteAnimationRunner getRunner() {
+ return mRunner;
+ }
+
+ public long getDuration() {
+ return mDuration;
+ }
+
+ public long getStatusBarTransitionDelay() {
+ return mStatusBarTransitionDelay;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongInterface(mRunner);
+ dest.writeLong(mDuration);
+ dest.writeLong(mStatusBarTransitionDelay);
+ }
+
+ public static final Creator<RemoteAnimationAdapter> CREATOR
+ = new Creator<RemoteAnimationAdapter>() {
+ public RemoteAnimationAdapter createFromParcel(Parcel in) {
+ return new RemoteAnimationAdapter(in);
+ }
+
+ public RemoteAnimationAdapter[] newArray(int size) {
+ return new RemoteAnimationAdapter[size];
+ }
+ };
+}
diff --git a/core/java/android/security/keystore/RecoveryData.aidl b/core/java/android/view/RemoteAnimationTarget.aidl
similarity index 76%
rename from core/java/android/security/keystore/RecoveryData.aidl
rename to core/java/android/view/RemoteAnimationTarget.aidl
index 4200de1..769bf5e 100644
--- a/core/java/android/security/keystore/RecoveryData.aidl
+++ b/core/java/android/view/RemoteAnimationTarget.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,10 +11,9 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package android.security.keystore;
+package android.view;
-/* @hide */
-parcelable RecoveryData;
+parcelable RemoteAnimationTarget;
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
new file mode 100644
index 0000000..f39e618
--- /dev/null
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import android.annotation.IntDef;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Describes an activity to be animated as part of a remote animation.
+ *
+ * @hide
+ */
+public class RemoteAnimationTarget implements Parcelable {
+
+ /**
+ * The app is in the set of opening apps of this transition.
+ */
+ public static final int MODE_OPENING = 0;
+
+ /**
+ * The app is in the set of closing apps of this transition.
+ */
+ public static final int MODE_CLOSING = 1;
+
+ @IntDef(prefix = { "MODE_" }, value = {
+ MODE_OPENING,
+ MODE_CLOSING
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Mode {}
+
+ /**
+ * The {@link Mode} to describe whether this app is opening or closing.
+ */
+ public final @Mode int mode;
+
+ /**
+ * The id of the task this app belongs to.
+ */
+ public final int taskId;
+
+ /**
+ * The {@link SurfaceControl} object to actually control the transform of the app.
+ */
+ public final SurfaceControl leash;
+
+ /**
+ * Whether the app is translucent and may reveal apps behind.
+ */
+ public final boolean isTranslucent;
+
+ /**
+ * The clip rect window manager applies when clipping the app's main surface in screen space
+ * coordinates. This is just a hint to the animation runner: If running a clip-rect animation,
+ * anything that extends beyond these bounds will not have any effect. This implies that any
+ * clip-rect animation should likely stop at these bounds.
+ */
+ public final Rect clipRect;
+
+ /**
+ * The index of the element in the tree in prefix order. This should be used for z-layering
+ * to preserve original z-layer order in the hierarchy tree assuming no "boosting" needs to
+ * happen.
+ */
+ public final int prefixOrderIndex;
+
+ /**
+ * The source position of the app, in screen spaces coordinates. If the position of the leash
+ * is modified from the controlling app, any animation transform needs to be offset by this
+ * amount.
+ */
+ public final Point position;
+
+ /**
+ * The bounds of the source container the app lives in, in screen space coordinates. If the crop
+ * of the leash is modified from the controlling app, it needs to take the source container
+ * bounds into account when calculating the crop.
+ */
+ public final Rect sourceContainerBounds;
+
+ public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
+ Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds) {
+ this.mode = mode;
+ this.taskId = taskId;
+ this.leash = leash;
+ this.isTranslucent = isTranslucent;
+ this.clipRect = new Rect(clipRect);
+ this.prefixOrderIndex = prefixOrderIndex;
+ this.position = new Point(position);
+ this.sourceContainerBounds = new Rect(sourceContainerBounds);
+ }
+
+ public RemoteAnimationTarget(Parcel in) {
+ taskId = in.readInt();
+ mode = in.readInt();
+ leash = in.readParcelable(null);
+ isTranslucent = in.readBoolean();
+ clipRect = in.readParcelable(null);
+ prefixOrderIndex = in.readInt();
+ position = in.readParcelable(null);
+ sourceContainerBounds = in.readParcelable(null);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(taskId);
+ dest.writeInt(mode);
+ dest.writeParcelable(leash, 0 /* flags */);
+ dest.writeBoolean(isTranslucent);
+ dest.writeParcelable(clipRect, 0 /* flags */);
+ dest.writeInt(prefixOrderIndex);
+ dest.writeParcelable(position, 0 /* flags */);
+ dest.writeParcelable(sourceContainerBounds, 0 /* flags */);
+ }
+
+ public static final Creator<RemoteAnimationTarget> CREATOR
+ = new Creator<RemoteAnimationTarget>() {
+ public RemoteAnimationTarget createFromParcel(Parcel in) {
+ return new RemoteAnimationTarget(in);
+ }
+
+ public RemoteAnimationTarget[] newArray(int size) {
+ return new RemoteAnimationTarget[size];
+ }
+ };
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index bd3be1b..f0b712b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4210,6 +4210,11 @@
*/
private static boolean sCanFocusZeroSized;
+ /**
+ * Always assign focus if a focusable View is available.
+ */
+ private static boolean sAlwaysAssignFocus;
+
private String mTransitionName;
static class TintInfo {
@@ -4827,6 +4832,8 @@
sCanFocusZeroSized = targetSdkVersion < Build.VERSION_CODES.P;
+ sAlwaysAssignFocus = targetSdkVersion < Build.VERSION_CODES.P;
+
sCompatibilityDone = true;
}
}
@@ -7017,8 +7024,8 @@
* Called when this view wants to give up focus. If focus is cleared
* {@link #onFocusChanged(boolean, int, android.graphics.Rect)} is called.
* <p>
- * <strong>Note:</strong> When a View clears focus the framework is trying
- * to give focus to the first focusable View from the top. Hence, if this
+ * <strong>Note:</strong> When not in touch-mode, the framework will try to give focus
+ * to the first focusable View from the top after focus is cleared. Hence, if this
* View is the first from the top that can take focus, then all callbacks
* related to clearing focus will be invoked after which the framework will
* give focus to this view.
@@ -7029,7 +7036,8 @@
System.out.println(this + " clearFocus()");
}
- clearFocusInternal(null, true, true);
+ final boolean refocus = sAlwaysAssignFocus || !isInTouchMode();
+ clearFocusInternal(null, true, refocus);
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index fe3b696..30f584c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -530,7 +530,7 @@
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
if (!sCompatibilityDone) {
- sAlwaysAssignFocus = true;
+ sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P;
sCompatibilityDone = true;
}
@@ -2337,7 +2337,7 @@
}
if (mFirst) {
- if (sAlwaysAssignFocus) {
+ if (sAlwaysAssignFocus || !isInTouchMode()) {
// handle first focus request
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "First: mView.hasFocus()=" + mView.hasFocus());
@@ -3608,7 +3608,7 @@
checkThread();
if (mView != null) {
if (!mView.hasFocus()) {
- if (sAlwaysAssignFocus) {
+ if (sAlwaysAssignFocus || !isInTouchMode()) {
v.requestFocus();
}
} else {
@@ -4211,10 +4211,7 @@
// find the best view to give focus to in this brave new non-touch-mode
// world
- final View focused = focusSearch(null, View.FOCUS_DOWN);
- if (focused != null) {
- return focused.requestFocus(View.FOCUS_DOWN);
- }
+ return mView.restoreDefaultFocus();
}
return false;
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 439e5df..ac5dbc4 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -4062,8 +4062,6 @@
public void noteWakupAlarmLocked(String packageName, int uid, WorkSource workSource,
String tag) {
- final int[] uids = new int[1];
- final String[] tags = new String[1];
if (workSource != null) {
for (int i = 0; i < workSource.size(); ++i) {
uid = workSource.get(i);
@@ -4074,9 +4072,8 @@
workSourceName != null ? workSourceName : packageName);
pkg.noteWakeupAlarmLocked(tag);
}
- uids[0] = workSource.get(i);
- tags[0] = workSource.getName(i);
- StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, uids, tags, tag);
+ StatsLog.write_non_chained(StatsLog.WAKEUP_ALARM_OCCURRED, workSource.get(i),
+ workSource.getName(i), tag);
}
ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4097,9 +4094,7 @@
BatteryStatsImpl.Uid.Pkg pkg = getPackageStatsLocked(uid, packageName);
pkg.noteWakeupAlarmLocked(tag);
}
- uids[0] = uid;
- tags[0] = null;
- StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, uids, tags, tag);
+ StatsLog.write_non_chained(StatsLog.WAKEUP_ALARM_OCCURRED, uid, null, tag);
}
}
@@ -4224,9 +4219,8 @@
StatsLog.write(
StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), type, name, 1);
} else {
- final int[] uids = new int[] { uid };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, type, name, 1);
+ StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null, type, name,
+ 1);
}
}
}
@@ -4268,9 +4262,8 @@
StatsLog.write(
StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), type, name, 0);
} else {
- final int[] uids = new int[] { uid };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, type, name, 0);
+ StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null, type, name,
+ 0);
}
}
}
@@ -4364,10 +4357,8 @@
}
public void noteLongPartialWakelockStart(String name, String historyName, int uid) {
- final int[] uids = new int[] { uid };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- uids, tags, name, historyName, 1);
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+ uid, null, name, historyName, 1);
uid = mapUid(uid);
noteLongPartialWakeLockStartInternal(name, historyName, uid);
@@ -4376,15 +4367,11 @@
public void noteLongPartialWakelockStartFromSource(String name, String historyName,
WorkSource workSource) {
final int N = workSource.size();
- final int[] uids = new int[1];
- final String[] tags = new String[1];
for (int i = 0; i < N; ++i) {
final int uid = mapUid(workSource.get(i));
noteLongPartialWakeLockStartInternal(name, historyName, uid);
- uids[0] = workSource.get(i);
- tags[0] = workSource.getName(i);
- StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uids, tags, name,
- historyName, 1);
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+ workSource.get(i), workSource.getName(i), name, historyName, 1);
}
final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4415,10 +4402,8 @@
}
public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
- int[] uids = new int[] { uid };
- String[] tags = new String[] { null };
- StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- uids, tags, name, historyName, 0);
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+ uid, null, name, historyName, 0);
uid = mapUid(uid);
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
@@ -4427,15 +4412,11 @@
public void noteLongPartialWakelockFinishFromSource(String name, String historyName,
WorkSource workSource) {
final int N = workSource.size();
- final int[] uids = new int[1];
- final String[] tags = new String[1];
for (int i = 0; i < N; ++i) {
final int uid = mapUid(workSource.get(i));
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
- uids[0] = workSource.get(i);
- tags[0] = workSource.getName(i);
- StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- uids, tags, name, historyName, 0);
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+ workSource.get(i), workSource.getName(i), name, historyName, 0);
}
final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -5420,11 +5401,10 @@
workChain.getUids(), workChain.getTags(), 1);
}
} else {
- final int[] uids = new int[] {uid};
- final String[] tags = new String[] {null};
- StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 1);
if (isUnoptimized) {
- StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
+ 1);
}
}
@@ -5470,11 +5450,10 @@
workChain.getUids(), workChain.getTags(), 0);
}
} else {
- final int[] uids = new int[] { uid };
- final String[] tags = new String[] {null};
- StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 0);
if (isUnoptimized) {
- StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
+ 0);
}
}
@@ -5547,14 +5526,11 @@
public void noteBluetoothScanResultsFromSourceLocked(WorkSource ws, int numNewResults) {
final int N = ws.size();
- final int[] uids = new int[1];
- final String[] tags = new String[1];
for (int i = 0; i < N; i++) {
int uid = mapUid(ws.get(i));
getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults);
- uids[0] = ws.get(i);
- tags[0] = ws.getName(i);
- StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED, uids, tags, numNewResults);
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_RESULT_RECEIVED, ws.get(i), ws.getName(i),
+ numNewResults);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5879,14 +5855,10 @@
public void noteFullWifiLockAcquiredFromSourceLocked(WorkSource ws) {
int N = ws.size();
- final int[] uids = new int[1];
- final String[] tags = new String[1];
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteFullWifiLockAcquiredLocked(uid);
- uids[0] = ws.get(i);
- tags[0] = ws.getName(i);
- StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 1);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5903,14 +5875,10 @@
public void noteFullWifiLockReleasedFromSourceLocked(WorkSource ws) {
int N = ws.size();
- final int[] uids = new int[1];
- final String[] tags = new String[1];
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteFullWifiLockReleasedLocked(uid);
- uids[0] = ws.get(i);
- tags[0] = ws.getName(i);
- StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 0);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5927,14 +5895,11 @@
public void noteWifiScanStartedFromSourceLocked(WorkSource ws) {
int N = ws.size();
- final int[] uids = new int[1];
- final String[] tags = new String[1];
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteWifiScanStartedLocked(uid);
- uids[0] = ws.get(i);
- tags[0] = ws.getName(i);
- StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
+ 1);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5951,14 +5916,11 @@
public void noteWifiScanStoppedFromSourceLocked(WorkSource ws) {
int N = ws.size();
- final int[] uids = new int[1];
- final String[] tags = new String[1];
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteWifiScanStoppedLocked(uid);
- uids[0] = ws.get(i);
- tags[0] = ws.getName(i);
- StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
+ 0);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6934,18 +6896,14 @@
public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 1);
}
public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mAudioTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
}
}
}
@@ -6953,9 +6911,7 @@
public void noteResetAudioLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
}
}
@@ -6969,18 +6925,15 @@
public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null, 1);
}
public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mVideoTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(),
+ null, 0);
}
}
}
@@ -6988,9 +6941,8 @@
public void noteResetVideoLocked(long elapsedRealtimeMs) {
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
+ 0);
}
}
@@ -7004,18 +6956,15 @@
public void noteFlashlightTurnedOnLocked(long elapsedRealtimeMs) {
createFlashlightTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.FLASHLIGHT_STATE_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,1);
}
public void noteFlashlightTurnedOffLocked(long elapsedRealtimeMs) {
if (mFlashlightTurnedOnTimer != null) {
mFlashlightTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mFlashlightTurnedOnTimer.isRunningLocked()) {
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.FLASHLIGHT_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
+ 0);
}
}
}
@@ -7023,9 +6972,7 @@
public void noteResetFlashlightLocked(long elapsedRealtimeMs) {
if (mFlashlightTurnedOnTimer != null) {
mFlashlightTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.FLASHLIGHT_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null, 0);
}
}
@@ -7039,18 +6986,14 @@
public void noteCameraTurnedOnLocked(long elapsedRealtimeMs) {
createCameraTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.CAMERA_STATE_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 1);
}
public void noteCameraTurnedOffLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mCameraTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.CAMERA_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
}
}
}
@@ -7058,9 +7001,7 @@
public void noteResetCameraLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.CAMERA_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
}
}
@@ -9622,9 +9563,7 @@
DualTimer t = mSyncStats.startObject(name);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.SYNC_STATE_CHANGED, uids, tags, name, 1);
+ StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 1);
}
}
@@ -9633,9 +9572,7 @@
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.SYNC_STATE_CHANGED, uids, tags, name, 0);
+ StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 0);
}
}
}
@@ -9644,9 +9581,8 @@
DualTimer t = mJobStats.startObject(name);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.SCHEDULED_JOB_STATE_CHANGED, uids, tags, name, 1);
+ StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
+ name, 1);
}
}
@@ -9655,9 +9591,8 @@
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
- StatsLog.write(StatsLog.SCHEDULED_JOB_STATE_CHANGED, uids, tags, name, 0);
+ StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
+ name, 0);
}
}
if (mBsi.mOnBatteryTimeBase.isRunning()) {
@@ -9768,12 +9703,11 @@
public void noteStartSensor(int sensor, long elapsedRealtimeMs) {
DualTimer t = getSensorTimerLocked(sensor, /* create= */ true);
t.startRunningLocked(elapsedRealtimeMs);
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
if (sensor == Sensor.GPS) {
- StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, uids, tags, 1);
+ StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, getUid(), null, 1);
} else {
- StatsLog.write(StatsLog.SENSOR_STATE_CHANGED, uids, tags, sensor, 1);
+ StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null, sensor,
+ 1);
}
}
@@ -9783,13 +9717,12 @@
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
- // TODO(statsd): Possibly use a worksource instead of a uid.
- final int[] uids = new int[] { getUid() };
- final String[] tags = new String[] { null };
if (sensor == Sensor.GPS) {
- StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, uids, tags, 0);
+ StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, getUid(), null,
+ 0);
} else {
- StatsLog.write(StatsLog.SENSOR_STATE_CHANGED, uids, tags, sensor, 0);
+ StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null,
+ sensor, 0);
}
}
}
diff --git a/core/java/com/android/internal/print/DualDumpOutputStream.java b/core/java/com/android/internal/print/DualDumpOutputStream.java
new file mode 100644
index 0000000..4b10ef2
--- /dev/null
+++ b/core/java/com/android/internal/print/DualDumpOutputStream.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.print;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+
+/**
+ * Dump either to a proto or a print writer using the same interface.
+ *
+ * <p>This mirrors the interface of {@link ProtoOutputStream}.
+ */
+public class DualDumpOutputStream {
+ private static final String LOG_TAG = DualDumpOutputStream.class.getSimpleName();
+
+ // When writing to a proto, the proto
+ private final @Nullable ProtoOutputStream mProtoStream;
+
+ // When printing in clear text, the writer
+ private final @Nullable IndentingPrintWriter mIpw;
+ // Temporary storage of data when printing to mIpw
+ private final LinkedList<DumpObject> mDumpObjects = new LinkedList<>();
+
+ private static abstract class Dumpable {
+ final String name;
+
+ private Dumpable(String name) {
+ this.name = name;
+ }
+
+ abstract void print(IndentingPrintWriter ipw, boolean printName);
+ }
+
+ private static class DumpObject extends Dumpable {
+ private final LinkedHashMap<String, ArrayList<Dumpable>> mSubObjects = new LinkedHashMap<>();
+
+ private DumpObject(String name) {
+ super(name);
+ }
+
+ @Override
+ void print(IndentingPrintWriter ipw, boolean printName) {
+ if (printName) {
+ ipw.println(name + "={");
+ } else {
+ ipw.println("{");
+ }
+ ipw.increaseIndent();
+
+ for (ArrayList<Dumpable> subObject: mSubObjects.values()) {
+ int numDumpables = subObject.size();
+
+ if (numDumpables == 1) {
+ subObject.get(0).print(ipw, true);
+ } else {
+ ipw.println(subObject.get(0).name + "=[");
+ ipw.increaseIndent();
+
+ for (int i = 0; i < numDumpables; i++) {
+ subObject.get(i).print(ipw, false);
+ }
+
+ ipw.decreaseIndent();
+ ipw.println("]");
+ }
+ }
+
+ ipw.decreaseIndent();
+ ipw.println("}");
+ }
+
+ /**
+ * Add new field / subobject to this object.
+ *
+ * <p>If a name is added twice, they will be printed as a array
+ *
+ * @param fieldName name of the field added
+ * @param d The dumpable to add
+ */
+ public void add(String fieldName, Dumpable d) {
+ ArrayList<Dumpable> l = mSubObjects.get(fieldName);
+
+ if (l == null) {
+ l = new ArrayList<>(1);
+ mSubObjects.put(fieldName, l);
+ }
+
+ l.add(d);
+ }
+ }
+
+ private static class DumpField extends Dumpable {
+ private final String mValue;
+
+ private DumpField(String name, String value) {
+ super(name);
+ this.mValue = value;
+ }
+
+ @Override
+ void print(IndentingPrintWriter ipw, boolean printName) {
+ if (printName) {
+ ipw.println(name + "=" + mValue);
+ } else {
+ ipw.println(mValue);
+ }
+ }
+ }
+
+
+ /**
+ * Create a new DualDumpOutputStream. Only one output should be set.
+ *
+ * @param proto If dumping to proto the {@link ProtoOutputStream}
+ * @param ipw If dumping to a print writer, the {@link IndentingPrintWriter}
+ */
+ public DualDumpOutputStream(@Nullable ProtoOutputStream proto,
+ @Nullable IndentingPrintWriter ipw) {
+ if ((proto == null) == (ipw == null)) {
+ Log.e(LOG_TAG, "Cannot dump to clear text and proto at once. Ignoring proto");
+ proto = null;
+ }
+
+ mProtoStream = proto;
+ mIpw = ipw;
+
+ if (!isProto()) {
+ // Add root object
+ mDumpObjects.add(new DumpObject(null));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, double val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, boolean val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, int val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, float val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, byte[] val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, Arrays.toString(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, long val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, @Nullable String val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public long start(@NonNull String fieldName, long fieldId) {
+ if (mProtoStream != null) {
+ return mProtoStream.start(fieldId);
+ } else {
+ DumpObject d = new DumpObject(fieldName);
+ mDumpObjects.getLast().add(fieldName, d);
+ mDumpObjects.addLast(d);
+ return System.identityHashCode(d);
+ }
+ }
+
+ public void end(long token) {
+ if (mProtoStream != null) {
+ mProtoStream.end(token);
+ } else {
+ if (System.identityHashCode(mDumpObjects.getLast()) != token) {
+ Log.w(LOG_TAG, "Unexpected token for ending " + mDumpObjects.getLast().name
+ + " at " + Arrays.toString(Thread.currentThread().getStackTrace()));
+ }
+ mDumpObjects.removeLast();
+ }
+ }
+
+ public void flush() {
+ if (mProtoStream != null) {
+ mProtoStream.flush();
+ } else {
+ if (mDumpObjects.size() == 1) {
+ mDumpObjects.getFirst().print(mIpw, false);
+
+ // Reset root object
+ mDumpObjects.clear();
+ mDumpObjects.add(new DumpObject(null));
+ }
+
+ mIpw.flush();
+ }
+ }
+
+ /**
+ * Add a dump from a different service into this dump.
+ *
+ * <p>Only for clear text dump. For proto dump use {@link #write(String, long, byte[])}.
+ *
+ * @param fieldName The name of the field
+ * @param nestedState The state of the dump
+ */
+ public void writeNested(@NonNull String fieldName, byte[] nestedState) {
+ if (mIpw == null) {
+ Log.w(LOG_TAG, "writeNested does not work for proto logging");
+ return;
+ }
+
+ mDumpObjects.getLast().add(fieldName,
+ new DumpField(fieldName, (new String(nestedState, StandardCharsets.UTF_8)).trim()));
+ }
+
+ /**
+ * @return {@code true} iff we are dumping to a proto
+ */
+ public boolean isProto() {
+ return mProtoStream != null;
+ }
+}
diff --git a/core/java/com/android/internal/print/DumpUtils.java b/core/java/com/android/internal/print/DumpUtils.java
index 28c7fc2..3192d5c 100644
--- a/core/java/com/android/internal/print/DumpUtils.java
+++ b/core/java/com/android/internal/print/DumpUtils.java
@@ -39,7 +39,6 @@
import android.service.print.PrinterIdProto;
import android.service.print.PrinterInfoProto;
import android.service.print.ResolutionProto;
-import android.util.proto.ProtoOutputStream;
/**
* Utilities for dumping print related proto buffer
@@ -49,13 +48,14 @@
* Write a string to a proto if the string is not {@code null}.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the string
* @param string The string to write
*/
- public static void writeStringIfNotNull(@NonNull ProtoOutputStream proto, long id,
- @Nullable String string) {
+ public static void writeStringIfNotNull(@NonNull DualDumpOutputStream proto, String idName,
+ long id, @Nullable String string) {
if (string != null) {
- proto.write(id, string);
+ proto.write(idName, id, string);
}
}
@@ -63,14 +63,15 @@
* Write a {@link ComponentName} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param component The component name to write
*/
- public static void writeComponentName(@NonNull ProtoOutputStream proto, long id,
- @NonNull ComponentName component) {
- long token = proto.start(id);
- proto.write(ComponentNameProto.PACKAGE_NAME, component.getPackageName());
- proto.write(ComponentNameProto.CLASS_NAME, component.getClassName());
+ public static void writeComponentName(@NonNull DualDumpOutputStream proto, String idName,
+ long id, @NonNull ComponentName component) {
+ long token = proto.start(idName, id);
+ proto.write("package_name", ComponentNameProto.PACKAGE_NAME, component.getPackageName());
+ proto.write("class_name", ComponentNameProto.CLASS_NAME, component.getClassName());
proto.end(token);
}
@@ -78,14 +79,16 @@
* Write a {@link PrinterId} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param printerId The printer id to write
*/
- public static void writePrinterId(@NonNull ProtoOutputStream proto, long id,
+ public static void writePrinterId(@NonNull DualDumpOutputStream proto, String idName, long id,
@NonNull PrinterId printerId) {
- long token = proto.start(id);
- writeComponentName(proto, PrinterIdProto.SERVICE_NAME, printerId.getServiceName());
- proto.write(PrinterIdProto.LOCAL_ID, printerId.getLocalId());
+ long token = proto.start(idName, id);
+ writeComponentName(proto, "service_name", PrinterIdProto.SERVICE_NAME,
+ printerId.getServiceName());
+ proto.write("local_id", PrinterIdProto.LOCAL_ID, printerId.getLocalId());
proto.end(token);
}
@@ -93,71 +96,76 @@
* Write a {@link PrinterCapabilitiesInfo} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param cap The capabilities to write
*/
public static void writePrinterCapabilities(@NonNull Context context,
- @NonNull ProtoOutputStream proto, long id, @NonNull PrinterCapabilitiesInfo cap) {
- long token = proto.start(id);
- writeMargins(proto, PrinterCapabilitiesProto.MIN_MARGINS, cap.getMinMargins());
+ @NonNull DualDumpOutputStream proto, String idName, long id,
+ @NonNull PrinterCapabilitiesInfo cap) {
+ long token = proto.start(idName, id);
+ writeMargins(proto, "min_margins", PrinterCapabilitiesProto.MIN_MARGINS,
+ cap.getMinMargins());
int numMediaSizes = cap.getMediaSizes().size();
for (int i = 0; i < numMediaSizes; i++) {
- writeMediaSize(context, proto, PrinterCapabilitiesProto.MEDIA_SIZES,
+ writeMediaSize(context, proto, "media_sizes", PrinterCapabilitiesProto.MEDIA_SIZES,
cap.getMediaSizes().get(i));
}
int numResolutions = cap.getResolutions().size();
for (int i = 0; i < numResolutions; i++) {
- writeResolution(proto, PrinterCapabilitiesProto.RESOLUTIONS,
+ writeResolution(proto, "resolutions", PrinterCapabilitiesProto.RESOLUTIONS,
cap.getResolutions().get(i));
}
if ((cap.getColorModes() & PrintAttributes.COLOR_MODE_MONOCHROME) != 0) {
- proto.write(PrinterCapabilitiesProto.COLOR_MODES,
+ proto.write("color_modes", PrinterCapabilitiesProto.COLOR_MODES,
PrintAttributesProto.COLOR_MODE_MONOCHROME);
}
if ((cap.getColorModes() & PrintAttributes.COLOR_MODE_COLOR) != 0) {
- proto.write(PrinterCapabilitiesProto.COLOR_MODES,
+ proto.write("color_modes", PrinterCapabilitiesProto.COLOR_MODES,
PrintAttributesProto.COLOR_MODE_COLOR);
}
if ((cap.getDuplexModes() & PrintAttributes.DUPLEX_MODE_NONE) != 0) {
- proto.write(PrinterCapabilitiesProto.DUPLEX_MODES,
+ proto.write("duplex_modes", PrinterCapabilitiesProto.DUPLEX_MODES,
PrintAttributesProto.DUPLEX_MODE_NONE);
}
if ((cap.getDuplexModes() & PrintAttributes.DUPLEX_MODE_LONG_EDGE) != 0) {
- proto.write(PrinterCapabilitiesProto.DUPLEX_MODES,
+ proto.write("duplex_modes", PrinterCapabilitiesProto.DUPLEX_MODES,
PrintAttributesProto.DUPLEX_MODE_LONG_EDGE);
}
if ((cap.getDuplexModes() & PrintAttributes.DUPLEX_MODE_SHORT_EDGE) != 0) {
- proto.write(PrinterCapabilitiesProto.DUPLEX_MODES,
+ proto.write("duplex_modes", PrinterCapabilitiesProto.DUPLEX_MODES,
PrintAttributesProto.DUPLEX_MODE_SHORT_EDGE);
}
proto.end(token);
}
-
/**
* Write a {@link PrinterInfo} to a proto.
*
* @param context The context used to resolve resources
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param info The printer info to write
*/
- public static void writePrinterInfo(@NonNull Context context, @NonNull ProtoOutputStream proto,
- long id, @NonNull PrinterInfo info) {
- long token = proto.start(id);
- writePrinterId(proto, PrinterInfoProto.ID, info.getId());
- proto.write(PrinterInfoProto.NAME, info.getName());
- proto.write(PrinterInfoProto.STATUS, info.getStatus());
- proto.write(PrinterInfoProto.DESCRIPTION, info.getDescription());
+ public static void writePrinterInfo(@NonNull Context context,
+ @NonNull DualDumpOutputStream proto, String idName, long id,
+ @NonNull PrinterInfo info) {
+ long token = proto.start(idName, id);
+ writePrinterId(proto, "id", PrinterInfoProto.ID, info.getId());
+ proto.write("name", PrinterInfoProto.NAME, info.getName());
+ proto.write("status", PrinterInfoProto.STATUS, info.getStatus());
+ proto.write("description", PrinterInfoProto.DESCRIPTION, info.getDescription());
PrinterCapabilitiesInfo cap = info.getCapabilities();
if (cap != null) {
- writePrinterCapabilities(context, proto, PrinterInfoProto.CAPABILITIES, cap);
+ writePrinterCapabilities(context, proto, "capabilities", PrinterInfoProto.CAPABILITIES,
+ cap);
}
proto.end(token);
@@ -168,16 +176,17 @@
*
* @param context The context used to resolve resources
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param mediaSize The media size to write
*/
- public static void writeMediaSize(@NonNull Context context, @NonNull ProtoOutputStream proto,
- long id, @NonNull PrintAttributes.MediaSize mediaSize) {
- long token = proto.start(id);
- proto.write(MediaSizeProto.ID, mediaSize.getId());
- proto.write(MediaSizeProto.LABEL, mediaSize.getLabel(context.getPackageManager()));
- proto.write(MediaSizeProto.HEIGHT_MILS, mediaSize.getHeightMils());
- proto.write(MediaSizeProto.WIDTH_MILS, mediaSize.getWidthMils());
+ public static void writeMediaSize(@NonNull Context context, @NonNull DualDumpOutputStream proto,
+ String idName, long id, @NonNull PrintAttributes.MediaSize mediaSize) {
+ long token = proto.start(idName, id);
+ proto.write("id", MediaSizeProto.ID, mediaSize.getId());
+ proto.write("label", MediaSizeProto.LABEL, mediaSize.getLabel(context.getPackageManager()));
+ proto.write("height_mils", MediaSizeProto.HEIGHT_MILS, mediaSize.getHeightMils());
+ proto.write("width_mils", MediaSizeProto.WIDTH_MILS, mediaSize.getWidthMils());
proto.end(token);
}
@@ -185,16 +194,17 @@
* Write a {@link PrintAttributes.Resolution} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param res The resolution to write
*/
- public static void writeResolution(@NonNull ProtoOutputStream proto, long id,
+ public static void writeResolution(@NonNull DualDumpOutputStream proto, String idName, long id,
@NonNull PrintAttributes.Resolution res) {
- long token = proto.start(id);
- proto.write(ResolutionProto.ID, res.getId());
- proto.write(ResolutionProto.LABEL, res.getLabel());
- proto.write(ResolutionProto.HORIZONTAL_DPI, res.getHorizontalDpi());
- proto.write(ResolutionProto.VERTICAL_DPI, res.getVerticalDpi());
+ long token = proto.start(idName, id);
+ proto.write("id", ResolutionProto.ID, res.getId());
+ proto.write("label", ResolutionProto.LABEL, res.getLabel());
+ proto.write("horizontal_DPI", ResolutionProto.HORIZONTAL_DPI, res.getHorizontalDpi());
+ proto.write("veritical_DPI", ResolutionProto.VERTICAL_DPI, res.getVerticalDpi());
proto.end(token);
}
@@ -202,16 +212,17 @@
* Write a {@link PrintAttributes.Margins} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param margins The margins to write
*/
- public static void writeMargins(@NonNull ProtoOutputStream proto, long id,
+ public static void writeMargins(@NonNull DualDumpOutputStream proto, String idName, long id,
@NonNull PrintAttributes.Margins margins) {
- long token = proto.start(id);
- proto.write(MarginsProto.TOP_MILS, margins.getTopMils());
- proto.write(MarginsProto.LEFT_MILS, margins.getLeftMils());
- proto.write(MarginsProto.RIGHT_MILS, margins.getRightMils());
- proto.write(MarginsProto.BOTTOM_MILS, margins.getBottomMils());
+ long token = proto.start(idName, id);
+ proto.write("top_mils", MarginsProto.TOP_MILS, margins.getTopMils());
+ proto.write("left_mils", MarginsProto.LEFT_MILS, margins.getLeftMils());
+ proto.write("right_mils", MarginsProto.RIGHT_MILS, margins.getRightMils());
+ proto.write("bottom_mils", MarginsProto.BOTTOM_MILS, margins.getBottomMils());
proto.end(token);
}
@@ -220,32 +231,34 @@
*
* @param context The context used to resolve resources
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param attributes The attributes to write
*/
public static void writePrintAttributes(@NonNull Context context,
- @NonNull ProtoOutputStream proto, long id, @NonNull PrintAttributes attributes) {
- long token = proto.start(id);
+ @NonNull DualDumpOutputStream proto, String idName, long id,
+ @NonNull PrintAttributes attributes) {
+ long token = proto.start(idName, id);
PrintAttributes.MediaSize mediaSize = attributes.getMediaSize();
if (mediaSize != null) {
- writeMediaSize(context, proto, PrintAttributesProto.MEDIA_SIZE, mediaSize);
+ writeMediaSize(context, proto, "media_size", PrintAttributesProto.MEDIA_SIZE, mediaSize);
}
- proto.write(PrintAttributesProto.IS_PORTRAIT, attributes.isPortrait());
+ proto.write("is_portrait", PrintAttributesProto.IS_PORTRAIT, attributes.isPortrait());
PrintAttributes.Resolution res = attributes.getResolution();
if (res != null) {
- writeResolution(proto, PrintAttributesProto.RESOLUTION, res);
+ writeResolution(proto, "resolution", PrintAttributesProto.RESOLUTION, res);
}
PrintAttributes.Margins minMargins = attributes.getMinMargins();
if (minMargins != null) {
- writeMargins(proto, PrintAttributesProto.MIN_MARGINS, minMargins);
+ writeMargins(proto, "min_margings", PrintAttributesProto.MIN_MARGINS, minMargins);
}
- proto.write(PrintAttributesProto.COLOR_MODE, attributes.getColorMode());
- proto.write(PrintAttributesProto.DUPLEX_MODE, attributes.getDuplexMode());
+ proto.write("color_mode", PrintAttributesProto.COLOR_MODE, attributes.getColorMode());
+ proto.write("duplex_mode", PrintAttributesProto.DUPLEX_MODE, attributes.getDuplexMode());
proto.end(token);
}
@@ -253,21 +266,22 @@
* Write a {@link PrintDocumentInfo} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param info The info to write
*/
- public static void writePrintDocumentInfo(@NonNull ProtoOutputStream proto, long id,
- @NonNull PrintDocumentInfo info) {
- long token = proto.start(id);
- proto.write(PrintDocumentInfoProto.NAME, info.getName());
+ public static void writePrintDocumentInfo(@NonNull DualDumpOutputStream proto, String idName,
+ long id, @NonNull PrintDocumentInfo info) {
+ long token = proto.start(idName, id);
+ proto.write("name", PrintDocumentInfoProto.NAME, info.getName());
int pageCount = info.getPageCount();
if (pageCount != PrintDocumentInfo.PAGE_COUNT_UNKNOWN) {
- proto.write(PrintDocumentInfoProto.PAGE_COUNT, pageCount);
+ proto.write("page_count", PrintDocumentInfoProto.PAGE_COUNT, pageCount);
}
- proto.write(PrintDocumentInfoProto.CONTENT_TYPE, info.getContentType());
- proto.write(PrintDocumentInfoProto.DATA_SIZE, info.getDataSize());
+ proto.write("content_type", PrintDocumentInfoProto.CONTENT_TYPE, info.getContentType());
+ proto.write("data_size", PrintDocumentInfoProto.DATA_SIZE, info.getDataSize());
proto.end(token);
}
@@ -275,14 +289,15 @@
* Write a {@link PageRange} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param range The range to write
*/
- public static void writePageRange(@NonNull ProtoOutputStream proto, long id,
+ public static void writePageRange(@NonNull DualDumpOutputStream proto, String idName, long id,
@NonNull PageRange range) {
- long token = proto.start(id);
- proto.write(PageRangeProto.START, range.getStart());
- proto.write(PageRangeProto.END, range.getEnd());
+ long token = proto.start(idName, id);
+ proto.write("start", PageRangeProto.START, range.getStart());
+ proto.write("end", PageRangeProto.END, range.getEnd());
proto.end(token);
}
@@ -291,64 +306,70 @@
*
* @param context The context used to resolve resources
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param printJobInfo The print job info to write
*/
- public static void writePrintJobInfo(@NonNull Context context, @NonNull ProtoOutputStream proto,
- long id, @NonNull PrintJobInfo printJobInfo) {
- long token = proto.start(id);
- proto.write(PrintJobInfoProto.LABEL, printJobInfo.getLabel());
+ public static void writePrintJobInfo(@NonNull Context context,
+ @NonNull DualDumpOutputStream proto, String idName, long id,
+ @NonNull PrintJobInfo printJobInfo) {
+ long token = proto.start(idName, id);
+ proto.write("label", PrintJobInfoProto.LABEL, printJobInfo.getLabel());
PrintJobId printJobId = printJobInfo.getId();
if (printJobId != null) {
- proto.write(PrintJobInfoProto.PRINT_JOB_ID, printJobId.flattenToString());
+ proto.write("print_job_id", PrintJobInfoProto.PRINT_JOB_ID,
+ printJobId.flattenToString());
}
int state = printJobInfo.getState();
if (state >= PrintJobInfoProto.STATE_CREATED && state <= PrintJobInfoProto.STATE_CANCELED) {
- proto.write(PrintJobInfoProto.STATE, state);
+ proto.write("state", PrintJobInfoProto.STATE, state);
} else {
- proto.write(PrintJobInfoProto.STATE, PrintJobInfoProto.STATE_UNKNOWN);
+ proto.write("state", PrintJobInfoProto.STATE, PrintJobInfoProto.STATE_UNKNOWN);
}
PrinterId printer = printJobInfo.getPrinterId();
if (printer != null) {
- writePrinterId(proto, PrintJobInfoProto.PRINTER, printer);
+ writePrinterId(proto, "printer", PrintJobInfoProto.PRINTER, printer);
}
String tag = printJobInfo.getTag();
if (tag != null) {
- proto.write(PrintJobInfoProto.TAG, tag);
+ proto.write("tag", PrintJobInfoProto.TAG, tag);
}
- proto.write(PrintJobInfoProto.CREATION_TIME, printJobInfo.getCreationTime());
+ proto.write("creation_time", PrintJobInfoProto.CREATION_TIME,
+ printJobInfo.getCreationTime());
PrintAttributes attributes = printJobInfo.getAttributes();
if (attributes != null) {
- writePrintAttributes(context, proto, PrintJobInfoProto.ATTRIBUTES, attributes);
+ writePrintAttributes(context, proto, "attributes", PrintJobInfoProto.ATTRIBUTES,
+ attributes);
}
PrintDocumentInfo docInfo = printJobInfo.getDocumentInfo();
if (docInfo != null) {
- writePrintDocumentInfo(proto, PrintJobInfoProto.DOCUMENT_INFO, docInfo);
+ writePrintDocumentInfo(proto, "document_info", PrintJobInfoProto.DOCUMENT_INFO,
+ docInfo);
}
- proto.write(PrintJobInfoProto.IS_CANCELING, printJobInfo.isCancelling());
+ proto.write("is_canceling", PrintJobInfoProto.IS_CANCELING, printJobInfo.isCancelling());
PageRange[] pages = printJobInfo.getPages();
if (pages != null) {
for (int i = 0; i < pages.length; i++) {
- writePageRange(proto, PrintJobInfoProto.PAGES, pages[i]);
+ writePageRange(proto, "pages", PrintJobInfoProto.PAGES, pages[i]);
}
}
- proto.write(PrintJobInfoProto.HAS_ADVANCED_OPTIONS,
+ proto.write("has_advanced_options", PrintJobInfoProto.HAS_ADVANCED_OPTIONS,
printJobInfo.getAdvancedOptions() != null);
- proto.write(PrintJobInfoProto.PROGRESS, printJobInfo.getProgress());
+ proto.write("progress", PrintJobInfoProto.PROGRESS, printJobInfo.getProgress());
CharSequence status = printJobInfo.getStatus(context.getPackageManager());
if (status != null) {
- proto.write(PrintJobInfoProto.STATUS, status.toString());
+ proto.write("status", PrintJobInfoProto.STATUS, status.toString());
}
proto.end(token);
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 31d22e0..b2bab6f 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -19,9 +19,9 @@
import android.app.PendingIntent;
import android.app.trust.IStrongAuthTracker;
import android.os.Bundle;
-import android.security.keystore.EntryRecoveryData;
-import android.security.keystore.RecoveryData;
-import android.security.keystore.RecoveryMetadata;
+import android.security.keystore.WrappedApplicationKey;
+import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.KeychainProtectionParameter;
import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.VerifyCredentialResponse;
@@ -64,7 +64,7 @@
// {@code ServiceSpecificException} may be thrown to signal an error, which caller can
// convert to {@code RecoveryManagerException}.
void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList);
- RecoveryData getRecoveryData(in byte[] account);
+ KeychainSnapshot getRecoveryData(in byte[] account);
byte[] generateAndStoreKey(String alias);
void removeKey(String alias);
void setSnapshotCreatedPendingIntent(in PendingIntent intent);
@@ -75,10 +75,10 @@
void setRecoverySecretTypes(in int[] secretTypes);
int[] getRecoverySecretTypes();
int[] getPendingRecoverySecretTypes();
- void recoverySecretAvailable(in RecoveryMetadata recoverySecret);
+ void recoverySecretAvailable(in KeychainProtectionParameter recoverySecret);
byte[] startRecoverySession(in String sessionId,
in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge,
- in List<RecoveryMetadata> secrets);
+ in List<KeychainProtectionParameter> secrets);
Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob,
- in List<EntryRecoveryData> applicationKeys);
+ in List<WrappedApplicationKey> applicationKeys);
}
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 79aa5ac..685fcaf 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -237,10 +237,22 @@
// Create the codec.
NinePatchPeeker peeker;
- std::unique_ptr<SkAndroidCodec> codec = SkAndroidCodec::MakeFromStream(
- std::move(stream), &peeker);
- if (!codec.get()) {
- return nullObjectReturn("SkAndroidCodec::MakeFromStream returned null");
+ std::unique_ptr<SkAndroidCodec> codec;
+ {
+ SkCodec::Result result;
+ std::unique_ptr<SkCodec> c = SkCodec::MakeFromStream(std::move(stream), &result,
+ &peeker);
+ if (!c) {
+ SkString msg;
+ msg.printf("Failed to create image decoder with message '%s'",
+ SkCodec::ResultToString(result));
+ return nullObjectReturn(msg.c_str());
+ }
+
+ codec = SkAndroidCodec::MakeFromCodec(std::move(c));
+ if (!codec) {
+ return nullObjectReturn("SkAndroidCodec::MakeFromCodec returned null");
+ }
}
// Do not allow ninepatch decodes to 565. In the past, decodes to 565
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index a0a4be4..0f36827 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -77,11 +77,27 @@
return nullptr;
}
std::unique_ptr<ImageDecoder> decoder(new ImageDecoder);
- decoder->mCodec = SkAndroidCodec::MakeFromStream(std::move(stream), &decoder->mPeeker);
+ SkCodec::Result result;
+ auto codec = SkCodec::MakeFromStream(std::move(stream), &result, &decoder->mPeeker);
+ if (!codec) {
+ switch (result) {
+ case SkCodec::kIncompleteInput:
+ env->ThrowNew(gIncomplete_class, "Incomplete input");
+ break;
+ default:
+ SkString msg;
+ msg.printf("Failed to create image decoder with message '%s'",
+ SkCodec::ResultToString(result));
+ doThrowIOE(env, msg.c_str());
+ break;
+ }
+
+ return nullptr;
+ }
+
+ decoder->mCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
if (!decoder->mCodec.get()) {
- // FIXME: (b/71578461) Use the error message from
- // SkCodec::MakeFromStream to report a more informative error message.
- doThrowIOE(env, "Failed to create an SkCodec");
+ doThrowIOE(env, "Could not create AndroidCodec");
return nullptr;
}
diff --git a/core/proto/android/server/forceappstandbytracker.proto b/core/proto/android/server/forceappstandbytracker.proto
index 8753bf7..c9f7d52 100644
--- a/core/proto/android/server/forceappstandbytracker.proto
+++ b/core/proto/android/server/forceappstandbytracker.proto
@@ -41,4 +41,13 @@
// Packages that are disallowed OP_RUN_ANY_IN_BACKGROUND.
repeated RunAnyInBackgroundRestrictedPackages run_any_in_background_restricted_packages = 5;
+
+ // Whether device is a small battery device
+ optional bool is_small_battery_device = 6;
+
+ // Whether force app standby for small battery device setting is enabled
+ optional bool force_all_apps_standby_for_small_battery = 7;
+
+ // Whether device is charging
+ optional bool is_charging = 8;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a3e8f1e..d2a22d0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3692,6 +3692,12 @@
<permission android:name="android.permission.MODIFY_QUIET_MODE"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to control remote animations. See
+ {@link ActivityOptions#makeRemoteAnimation}
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"
+ android:protectionLevel="signature|privileged" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e3a910f..3b02a96 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3249,4 +3249,7 @@
<string name="config_fontFamilyButton">@string/font_family_button_material</string>
<string translatable="false" name="config_batterySaverDeviceSpecificConfig"></string>
+
+ <!-- Package name that should be granted Notification Assistant access -->
+ <string name="config_defaultAssistantAccessPackage" translatable="false">android.ext.services</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 50dc384..638f1b2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3216,4 +3216,6 @@
<java-symbol type="string" name="harmful_app_warning_uninstall" />
<java-symbol type="string" name="harmful_app_warning_launch_anyway" />
<java-symbol type="string" name="harmful_app_warning_title" />
+
+ <java-symbol type="string" name="config_defaultAssistantAccessPackage" />
</resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 410bee0..2b3969f 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -212,6 +212,7 @@
Settings.Global.FANCY_IME_ANIMATIONS,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
Settings.Global.FORCED_APP_STANDBY_ENABLED,
+ Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
Settings.Global.FSTRIM_MANDATORY_INTERVAL,
Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
Settings.Global.GLOBAL_HTTP_PROXY_HOST,
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 4732bec..c0958cd 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -75,6 +75,7 @@
<privapp-permissions package="com.android.launcher3">
<permission name="android.permission.BIND_APPWIDGET"/>
+ <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
<permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
</privapp-permissions>
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 419e2b7..d13e05c 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -239,11 +239,12 @@
};
/**
- * Supplied to onPartialImage if the provided data is incomplete.
+ * Used if the provided data is incomplete.
*
- * Will never be thrown by ImageDecoder.
+ * May be thrown if there is nothing to display.
*
- * There may be a partial image to display.
+ * If supplied to onPartialImage, there may be a correct partial image to
+ * display.
*/
public static class IncompleteException extends IOException {};
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
index e0a3f6c..6c74418 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -59,7 +59,9 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.HandlerCaller;
+import com.android.internal.print.DualDumpOutputStream;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.printspooler.R;
import com.android.printspooler.util.ApprovedPrintServices;
@@ -159,44 +161,11 @@
return new PrintSpooler();
}
- private void dumpLocked(PrintWriter pw, String[] args) {
- String prefix = (args.length > 0) ? args[0] : "";
- String tab = " ";
-
- pw.append(prefix).append("print jobs:").println();
- final int printJobCount = mPrintJobs.size();
- for (int i = 0; i < printJobCount; i++) {
- PrintJobInfo printJob = mPrintJobs.get(i);
- pw.append(prefix).append(tab).append(printJob.toString());
- pw.println();
- }
-
- pw.append(prefix).append("print job files:").println();
- File[] files = getFilesDir().listFiles();
- if (files != null) {
- final int fileCount = files.length;
- for (int i = 0; i < fileCount; i++) {
- File file = files[i];
- if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
- pw.append(prefix).append(tab).append(file.getName()).println();
- }
- }
- }
-
- pw.append(prefix).append("approved print services:").println();
- Set<String> approvedPrintServices = (new ApprovedPrintServices(this)).getApprovedServices();
- if (approvedPrintServices != null) {
- for (String approvedService : approvedPrintServices) {
- pw.append(prefix).append(tab).append(approvedService).println();
- }
- }
- }
-
- private void dumpLocked(@NonNull ProtoOutputStream proto) {
+ private void dumpLocked(@NonNull DualDumpOutputStream dumpStream) {
int numPrintJobs = mPrintJobs.size();
for (int i = 0; i < numPrintJobs; i++) {
- writePrintJobInfo(this, proto, PrintSpoolerInternalStateProto.PRINT_JOBS,
- mPrintJobs.get(i));
+ writePrintJobInfo(this, dumpStream, "print_jobs",
+ PrintSpoolerInternalStateProto.PRINT_JOBS, mPrintJobs.get(i));
}
File[] files = getFilesDir().listFiles();
@@ -204,7 +173,8 @@
for (int i = 0; i < files.length; i++) {
File file = files[i];
if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
- proto.write(PrintSpoolerInternalStateProto.PRINT_JOB_FILES, file.getName());
+ dumpStream.write("print_job_files",
+ PrintSpoolerInternalStateProto.PRINT_JOB_FILES, file.getName());
}
}
}
@@ -214,13 +184,13 @@
for (String approvedService : approvedPrintServices) {
ComponentName componentName = ComponentName.unflattenFromString(approvedService);
if (componentName != null) {
- writeComponentName(proto, PrintSpoolerInternalStateProto.APPROVED_SERVICES,
- componentName);
+ writeComponentName(dumpStream, "approved_services",
+ PrintSpoolerInternalStateProto.APPROVED_SERVICES, componentName);
}
}
}
- proto.flush();
+ dumpStream.flush();
}
@Override
@@ -244,9 +214,15 @@
try {
synchronized (mLock) {
if (dumpAsProto) {
- dumpLocked(new ProtoOutputStream(fd));
+ dumpLocked(new DualDumpOutputStream(new ProtoOutputStream(fd), null));
} else {
- dumpLocked(pw, args);
+ try (FileOutputStream out = new FileOutputStream(fd)) {
+ try (PrintWriter w = new PrintWriter(out)) {
+ dumpLocked(new DualDumpOutputStream(null, new IndentingPrintWriter(w,
+ " ")));
+ }
+ } catch (IOException ignored) {
+ }
}
}
} finally {
diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml
index 287a888..fd4d296 100644
--- a/packages/SettingsProvider/AndroidManifest.xml
+++ b/packages/SettingsProvider/AndroidManifest.xml
@@ -8,6 +8,7 @@
android:process="system"
android:backupAgent="SettingsBackupAgent"
android:killAfterRestore="false"
+ android:restoreAnyVersion="true"
android:icon="@mipmap/ic_launcher_settings"
android:defaultToDeviceProtectedStorage="true"
android:directBootAware="true">
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index ae88227..c7ba4d6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -31,10 +31,12 @@
import android.net.Uri;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.BackupUtils;
import android.util.Log;
@@ -50,6 +52,7 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -147,6 +150,13 @@
// stored in the full-backup tarfile as well, so should not be changed.
private static final String STAGE_FILE = "flattened-data";
+ // List of keys that support restore to lower version of the SDK, introduced in Android P
+ private static final ArraySet<String> RESTORE_FROM_HIGHER_SDK_INT_SUPPORTED_KEYS =
+ new ArraySet<String>(Arrays.asList(new String[] {
+ KEY_NETWORK_POLICIES,
+ KEY_WIFI_NEW_CONFIG,
+ }));
+
private SettingsHelper mSettingsHelper;
private WifiManager mWifiManager;
@@ -209,6 +219,10 @@
public void onRestore(BackupDataInput data, int appVersionCode,
ParcelFileDescriptor newState) throws IOException {
+ if (DEBUG) {
+ Log.d(TAG, "onRestore(): appVersionCode: " + appVersionCode
+ + "; Build.VERSION.SDK_INT: " + Build.VERSION.SDK_INT);
+ }
// versionCode of com.android.providers.settings corresponds to SDK_INT
mRestoredFromSdkInt = appVersionCode;
@@ -221,6 +235,15 @@
while (data.readNextHeader()) {
final String key = data.getKey();
final int size = data.getDataSize();
+
+ // bail out of restoring from higher SDK_INT version for unsupported keys
+ if (appVersionCode > Build.VERSION.SDK_INT
+ && !RESTORE_FROM_HIGHER_SDK_INT_SUPPORTED_KEYS.contains(key)) {
+ Log.w(TAG, "Not restoring unrecognized key '"
+ + key + "' from future version " + appVersionCode);
+ continue;
+ }
+
switch (key) {
case KEY_SYSTEM :
restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index c52c0aa..61f7fe8 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -108,6 +108,7 @@
public Supplier<Icon> iconSupplier;
public int state = Tile.STATE_ACTIVE;
public CharSequence label;
+ public CharSequence secondaryLabel;
public CharSequence contentDescription;
public CharSequence dualLabelContentDescription;
public boolean disabledByPolicy;
@@ -122,6 +123,7 @@
final boolean changed = !Objects.equals(other.icon, icon)
|| !Objects.equals(other.iconSupplier, iconSupplier)
|| !Objects.equals(other.label, label)
+ || !Objects.equals(other.secondaryLabel, secondaryLabel)
|| !Objects.equals(other.contentDescription, contentDescription)
|| !Objects.equals(other.dualLabelContentDescription,
dualLabelContentDescription)
@@ -135,6 +137,7 @@
other.icon = icon;
other.iconSupplier = iconSupplier;
other.label = label;
+ other.secondaryLabel = secondaryLabel;
other.contentDescription = contentDescription;
other.dualLabelContentDescription = dualLabelContentDescription;
other.expandedAccessibilityClassName = expandedAccessibilityClassName;
@@ -156,6 +159,7 @@
sb.append(",icon=").append(icon);
sb.append(",iconSupplier=").append(iconSupplier);
sb.append(",label=").append(label);
+ sb.append(",secondaryLabel=").append(secondaryLabel);
sb.append(",contentDescription=").append(contentDescription);
sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription);
sb.append(",expandedAccessibilityClassName=").append(expandedAccessibilityClassName);
diff --git a/packages/SystemUI/res/drawable/smart_reply_button_background.xml b/packages/SystemUI/res/drawable/smart_reply_button_background.xml
new file mode 100644
index 0000000..1cd1451
--- /dev/null
+++ b/packages/SystemUI/res/drawable/smart_reply_button_background.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/notification_ripple_untinted_color">
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/smart_reply_button_corner_radius"/>
+ <solid android:color="@color/smart_reply_button_background"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/layout/smart_reply_button.xml b/packages/SystemUI/res/layout/smart_reply_button.xml
new file mode 100644
index 0000000..4ac41d5
--- /dev/null
+++ b/packages/SystemUI/res/layout/smart_reply_button.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@android:style/Widget.Material.Button.Borderless.Small"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/smart_reply_button_spacing"
+ android:paddingVertical="@dimen/smart_reply_button_padding_vertical"
+ android:paddingHorizontal="@dimen/smart_reply_button_corner_radius"
+ android:background="@drawable/smart_reply_button_background"
+ android:gravity="center"
+ android:fontFamily="sans-serif"
+ android:textSize="@dimen/smart_reply_button_font_size"
+ android:textColor="@color/smart_reply_button_text"
+ android:textStyle="normal"
+ android:singleLine="true"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/smart_reply_view.xml b/packages/SystemUI/res/layout/smart_reply_view.xml
new file mode 100644
index 0000000..6d53386
--- /dev/null
+++ b/packages/SystemUI/res/layout/smart_reply_view.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- LinearLayout -->
+<com.android.systemui.statusbar.policy.SmartReplyView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/smart_reply_view"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="end">
+ <!-- smart_reply_button(s) will be added here. -->
+</com.android.systemui.statusbar.policy.SmartReplyView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 7f37087..4614999 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -54,6 +54,18 @@
android:paddingStart="8dp"
/>
+ <ImageButton
+ android:id="@+id/helper"
+ android:layout_width="48dp"
+ android:layout_height="@*android:dimen/notification_header_height"
+ android:layout_gravity="top|end"
+ android:layout_marginEnd="6dp"
+ android:src="@drawable/ic_dnd"
+ android:tint="#FF0000"
+ android:background="@drawable/ripple_drawable"
+ android:visibility="visible"
+ />
+
<ViewStub
android:layout="@layout/notification_children_container"
android:id="@+id/child_container_stub"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index f0d2346..748c9a5 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -17,130 +17,76 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:background="@android:color/transparent"
android:theme="@style/qs_theme"
android:clipChildren="false" >
- <RelativeLayout
+ <LinearLayout
android:id="@+id/volume_dialog"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingTop="@dimen/volume_row_padding_bottom"
- android:background="@drawable/rounded_full_bg_bottom"
+ android:layout_gravity="center_vertical|end"
+ android:minWidth="@dimen/volume_dialog_panel_width"
+ android:background="@android:color/transparent"
+ android:layout_margin="12dp"
android:translationZ="8dp"
+ android:orientation="vertical"
android:clipChildren="false" >
<LinearLayout
- android:id="@+id/volume_dialog_content"
- android:layout_width="match_parent"
+ android:id="@+id/volume_dialog_rows"
+ android:layout_width="@dimen/volume_dialog_panel_width"
android:layout_height="wrap_content"
- android:layout_toStartOf="@id/expand"
android:clipChildren="false"
android:clipToPadding="false"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:background="@drawable/rounded_bg_full"
+ android:orientation="horizontal" >
+ <!-- volume rows added and removed here! :-) -->
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/footer"
+ android:layout_width="@dimen/volume_dialog_panel_width"
+ android:layout_height="@dimen/volume_dialog_panel_width"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="6dp"
+ android:layout_below="@id/volume_dialog_rows"
+ android:background="@drawable/rounded_bg_full"
+ android:gravity="center"
android:orientation="vertical" >
- <LinearLayout
- android:id="@+id/volume_dialog_rows"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
- <!-- volume rows added and removed here! :-) -->
- </LinearLayout>
-
-
- </LinearLayout>
- <LinearLayout
- android:id="@+id/expand"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_alignParentEnd="true"
- android:layout_alignParentTop="true"
- android:layout_marginEnd="@dimen/volume_expander_margin_end" >
<TextView
+ android:id="@+id/ringer_title"
+ android:text="@string/ring_toggle_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
- android:textAppearance="@style/TextAppearance.Volume.Header" />
+ android:layout_centerVertical="true"
+ android:textColor="?android:attr/colorControlNormal"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
<com.android.keyguard.AlphaOptimizedImageButton
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/volume_expand_button"
- style="@style/VolumeButtons"
- android:layout_width="@dimen/volume_button_size"
- android:layout_height="@dimen/volume_button_size"
- android:clickable="true"
- android:soundEffectsEnabled="false"
- android:src="@drawable/ic_volume_expand_animation"
- android:background="@drawable/ripple_drawable"
- tools:ignore="RtlHardcoded" />
- </LinearLayout>
- <RelativeLayout
- android:id="@+id/footer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:layout_below="@id/volume_dialog_content"
- android:layout_margin="10dp">
- <!-- special row for ringer mode -->
- <RelativeLayout
- android:id="@+id/ringer_mode"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/rounded_bg_full"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:layout_toStartOf="@id/output_chooser"
- android:layout_margin="10dp">
-
- <com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/ringer_icon"
- style="@style/VolumeButtons"
- android:background="?android:selectableItemBackgroundBorderless"
- android:layout_width="@dimen/volume_button_size"
- android:layout_height="@dimen/volume_button_size"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:soundEffectsEnabled="false" />
-
- <TextView
- android:id="@+id/ringer_title"
- android:text="@string/ring_toggle_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="1"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:layout_toEndOf="@+id/ringer_icon"
- android:layout_marginStart="64dp"
- android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:paddingStart="@dimen/volume_row_header_padding_start" />
-
- <TextView
- android:id="@+id/ringer_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- android:layout_marginEnd="14dp"
- android:maxLines="1"
- android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall" />
-
- </RelativeLayout>
- <com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/output_chooser"
+ android:id="@+id/ringer_icon"
style="@style/VolumeButtons"
android:background="?android:selectableItemBackgroundBorderless"
android:layout_width="@dimen/volume_button_size"
android:layout_height="@dimen/volume_button_size"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- android:src="@drawable/ic_settings_bluetooth"
+ android:tint="?android:attr/colorAccent"
android:soundEffectsEnabled="false" />
- </RelativeLayout>
- </RelativeLayout>
+
+ <TextView
+ android:id="@+id/ringer_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/colorControlNormal"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ </LinearLayout>
+ </LinearLayout>
</com.android.systemui.volume.VolumeUiLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index bf76e78..3590b76 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -15,48 +15,70 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/volume_row_height"
- android:clipChildren="false"
- android:clipToPadding="false"
+ android:tag="row"
+ android:layout_height="wrap_content"
+ android:layout_width="@dimen/volume_dialog_panel_width"
+ android:clipChildren="true"
+ android:clipToPadding="true"
android:theme="@style/qs_theme"
+ android:gravity="center"
android:orientation="vertical" >
- <TextView
- android:id="@+id/volume_row_header"
+ <LinearLayout
+ android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="1"
- android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:paddingStart="@dimen/volume_row_header_padding_start" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="@dimen/volume_row_slider_height"
- android:orientation="horizontal"
- android:paddingStart="@dimen/volume_row_padding_start" >
+ android:gravity="center"
+ android:padding="10dp">
+ <TextView
+ android:id="@+id/volume_row_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/colorControlNormal"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ <TextView
+ android:id="@+id/volume_row_connected_device"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary" />
<com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/volume_row_icon"
- style="@style/VolumeButtons"
- android:layout_width="@dimen/volume_button_size"
- android:layout_height="@dimen/volume_button_size"
- android:soundEffectsEnabled="false" />
-
- <SeekBar
- android:id="@+id/volume_row_slider"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignWithParentIfMissing="true"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:paddingStart="@dimen/volume_row_slider_padding_start"/>
+ android:id="@+id/output_chooser"
+ style="@style/VolumeButtons"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:layout_width="@dimen/volume_button_size"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:src="@drawable/ic_volume_expand_animation"
+ android:soundEffectsEnabled="false" />
</LinearLayout>
+ <FrameLayout
+ android:id="@+id/volume_row_slider_frame"
+ android:padding="10dp"
+ android:layout_width="@dimen/volume_dialog_panel_width"
+ android:layout_height="150dp">
+ <SeekBar
+ android:id="@+id/volume_row_slider"
+ android:padding="0dp"
+ android:layout_margin="0dp"
+ android:layout_width="150dp"
+ android:layout_height="@dimen/volume_dialog_panel_width"
+ android:layout_gravity="center"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:rotation="270" />
+ </FrameLayout>
- <Space
- android:id="@+id/spacer"
- android:layout_width="match_parent"
- android:layout_height="@dimen/volume_row_padding_bottom"/>
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/volume_row_icon"
+ style="@style/VolumeButtons"
+ android:padding="10dp"
+ android:layout_width="@dimen/volume_button_size"
+ android:layout_height="@dimen/volume_button_size"
+ android:soundEffectsEnabled="false" />
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index f5be337..0f4c3b8 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -156,4 +156,6 @@
<color name="zen_introduction">#ffffffff</color>
+ <color name="smart_reply_button_text">#ff4285f4</color><!-- blue 500 -->
+ <color name="smart_reply_button_background">#fff7f7f7</color>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 39ed08e..7a670fd 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -264,7 +264,7 @@
<!-- The width of the panel that holds the quick settings. -->
<dimen name="qs_panel_width">@dimen/notification_panel_width</dimen>
- <dimen name="volume_dialog_panel_width">315dp</dimen>
+ <dimen name="volume_dialog_panel_width">120dp</dimen>
<!-- Gravity for the notification panel -->
<integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top -->
@@ -882,4 +882,9 @@
<!-- Home button padding for sizing -->
<dimen name="home_padding">15dp</dimen>
+ <!-- Smart reply button -->
+ <dimen name="smart_reply_button_corner_radius">24dip</dimen>
+ <dimen name="smart_reply_button_spacing">8dp</dimen>
+ <dimen name="smart_reply_button_padding_vertical">4dp</dimen>
+ <dimen name="smart_reply_button_font_size">14sp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 263dac0..a226f3c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -18,6 +18,7 @@
import android.content.res.Configuration;
import android.service.quicksettings.Tile;
import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -28,6 +29,7 @@
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
+import com.android.systemui.R.id;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
@@ -36,8 +38,10 @@
/** View that represents a standard quick settings tile. **/
public class QSTileView extends QSTileBaseView {
+ private static final boolean DUAL_TARGET_ALLOWED = false;
private View mDivider;
protected TextView mLabel;
+ private TextView mSecondLine;
private ImageView mPadLock;
private int mState;
private ViewGroup mLabelContainer;
@@ -86,6 +90,8 @@
mDivider = mLabelContainer.findViewById(R.id.underline);
mExpandIndicator = mLabelContainer.findViewById(R.id.expand_indicator);
mExpandSpace = mLabelContainer.findViewById(R.id.expand_space);
+ mSecondLine = mLabelContainer.findViewById(R.id.app_label);
+ mSecondLine.setAlpha(.6f);
addView(mLabelContainer);
}
@@ -103,14 +109,20 @@
mState = state.state;
mLabel.setText(state.label);
}
- mExpandIndicator.setVisibility(state.dualTarget ? View.VISIBLE : View.GONE);
- mExpandSpace.setVisibility(state.dualTarget ? View.VISIBLE : View.GONE);
- mLabelContainer.setContentDescription(state.dualTarget ? state.dualLabelContentDescription
+ if (!Objects.equal(mSecondLine.getText(), state.secondaryLabel)) {
+ mSecondLine.setText(state.secondaryLabel);
+ mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) ? View.GONE
+ : View.VISIBLE);
+ }
+ boolean dualTarget = DUAL_TARGET_ALLOWED && state.dualTarget;
+ mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
+ mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
+ mLabelContainer.setContentDescription(dualTarget ? state.dualLabelContentDescription
: null);
- if (state.dualTarget != mLabelContainer.isClickable()) {
- mLabelContainer.setClickable(state.dualTarget);
- mLabelContainer.setLongClickable(state.dualTarget);
- mLabelContainer.setBackground(state.dualTarget ? newTileBackground() : null);
+ if (dualTarget != mLabelContainer.isClickable()) {
+ mLabelContainer.setClickable(dualTarget);
+ mLabelContainer.setLongClickable(dualTarget);
+ mLabelContainer.setBackground(dualTarget ? newTileBackground() : null);
}
mLabel.setEnabled(!state.disabledByPolicy);
mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 0e4a9fe..fff9f8e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -125,11 +125,11 @@
state.slash = new SlashState();
}
state.slash.isSlashed = !enabled;
+ state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
if (enabled) {
- state.label = null;
if (connected) {
state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_connected);
- state.label = mController.getLastDeviceName();
+ state.secondaryLabel = mController.getLastDeviceName();
CachedBluetoothDevice lastDevice = mController.getLastDevice();
if (lastDevice != null) {
int batteryLevel = lastDevice.getBatteryLevel();
@@ -140,25 +140,20 @@
}
}
state.contentDescription = mContext.getString(
- R.string.accessibility_bluetooth_name, state.label);
+ R.string.accessibility_bluetooth_name, state.secondaryLabel);
} else if (state.isTransient) {
state.icon = ResourceIcon.get(R.drawable.ic_bluetooth_transient_animation);
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_bluetooth_connecting);
- state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
} else {
state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_on);
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_bluetooth_on) + ","
+ mContext.getString(R.string.accessibility_not_connected);
}
- if (TextUtils.isEmpty(state.label)) {
- state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
- }
state.state = Tile.STATE_ACTIVE;
} else {
state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_on);
- state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_bluetooth_off);
state.state = Tile.STATE_INACTIVE;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index d1e6dcc..bf8a64c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -33,6 +33,7 @@
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.util.FloatProperty;
@@ -173,6 +174,7 @@
private FalsingManager mFalsingManager;
private AboveShelfChangedListener mAboveShelfChangedListener;
private HeadsUpManager mHeadsUpManager;
+ private View mHelperButton;
private boolean mJustClicked;
private boolean mIconAnimationRunning;
@@ -387,6 +389,9 @@
updateLimits();
updateIconVisibilities();
updateShelfIconColor();
+
+ showBlockingHelper(mEntry.userSentiment ==
+ NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
}
@VisibleForTesting
@@ -1318,6 +1323,10 @@
requestLayout();
}
+ public void showBlockingHelper(boolean show) {
+ mHelperButton.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -1325,6 +1334,12 @@
mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
+ final NotificationGutsManager gutsMan = Dependency.get(NotificationGutsManager.class);
+ mHelperButton = findViewById(R.id.helper);
+ mHelperButton.setOnClickListener(view -> {
+ doLongClickCallback();
+ });
+
for (NotificationContentView l : mLayouts) {
l.setExpandClickListener(mExpandClickListener);
l.setContainingNotification(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index d0417b5..7e0dba5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -86,6 +86,8 @@
public RemoteViews cachedAmbientContentView;
public CharSequence remoteInputText;
public List<SnoozeCriterion> snoozeCriteria;
+ public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
+
private int mCachedContrastColor = COLOR_INVALID;
private int mCachedContrastColorIsFor = COLOR_INVALID;
private InflationTask mRunningTask = null;
@@ -463,6 +465,7 @@
}
entry.channel = getChannel(entry.key);
entry.snoozeCriteria = getSnoozeCriteria(entry.key);
+ entry.userSentiment = mTmpRanking.getUserSentiment();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
new file mode 100644
index 0000000..1dcdf63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -0,0 +1,63 @@
+package com.android.systemui.statusbar.policy;
+
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+
+/** View which displays smart reply buttons in notifications. */
+public class SmartReplyView extends LinearLayout {
+
+ private static final String TAG = "SmartReplyView";
+
+ public SmartReplyView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setRepliesFromRemoteInput(RemoteInput remoteInput, PendingIntent pendingIntent) {
+ removeAllViews();
+ if (remoteInput != null && pendingIntent != null) {
+ CharSequence[] choices = remoteInput.getChoices();
+ if (choices != null) {
+ for (CharSequence choice : choices) {
+ Button replyButton = inflateReplyButton(
+ getContext(), this, choice, remoteInput, pendingIntent);
+ addView(replyButton);
+ }
+ }
+ }
+ }
+
+ public static SmartReplyView inflate(Context context, ViewGroup root) {
+ return (SmartReplyView)
+ LayoutInflater.from(context).inflate(R.layout.smart_reply_view, root, false);
+ }
+
+ private static Button inflateReplyButton(Context context, ViewGroup root, CharSequence choice,
+ RemoteInput remoteInput, PendingIntent pendingIntent) {
+ Button b = (Button) LayoutInflater.from(context).inflate(
+ R.layout.smart_reply_button, root, false);
+ b.setText(choice);
+ b.setOnClickListener(view -> {
+ Bundle results = new Bundle();
+ results.putString(remoteInput.getResultKey(), choice.toString());
+ Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ RemoteInput.addResultsToIntent(new RemoteInput[]{remoteInput}, intent, results);
+ try {
+ pendingIntent.send(context, 0, intent);
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "Unable to send smart reply", e);
+ }
+ });
+ return b;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index bc98140..efa8386 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -52,7 +52,7 @@
public static final String VOLUME_UP_SILENT = "sysui_volume_up_silent";
public static final String VOLUME_SILENT_DO_NOT_DISTURB = "sysui_do_not_disturb";
- public static final boolean DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT = true;
+ public static final boolean DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT = false;
public static final boolean DEFAULT_VOLUME_UP_TO_EXIT_SILENT = true;
public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = true;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 7b91f14..5a19a76 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -20,6 +20,7 @@
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
import static com.android.systemui.volume.Events.DISMISS_REASON_OUTPUT_CHOOSER;
+import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
import static com.android.systemui.volume.Events.DISMISS_REASON_TOUCH_OUTSIDE;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -30,14 +31,13 @@
import android.app.KeyguardManager;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
-import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.os.Debug;
@@ -45,9 +45,8 @@
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.provider.Settings;
import android.provider.Settings.Global;
-import android.transition.AutoTransition;
-import android.transition.TransitionManager;
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -72,7 +71,6 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.plugins.VolumeDialog;
import com.android.systemui.plugins.VolumeDialogController;
@@ -104,7 +102,6 @@
private CustomDialog mDialog;
private ViewGroup mDialogView;
private ViewGroup mDialogRowsView;
- private ImageButton mExpandButton;
private ImageButton mRingerIcon;
private ImageButton mOutputChooser;
private TextView mRingerStatus;
@@ -120,8 +117,6 @@
private final ColorStateList mInactiveSliderTint;
private boolean mShowing;
- private boolean mExpanded;
- private boolean mExpandButtonAnimationRunning;
private boolean mShowA11yStream;
private int mActiveStream;
@@ -182,11 +177,11 @@
mDialog.setContentView(R.layout.volume_dialog);
mDialog.setOnShowListener(dialog -> {
- mDialogView.setTranslationY(-mDialogView.getHeight());
+ mDialogView.setTranslationX(mDialogView.getWidth() / 2);
mDialogView.setAlpha(0);
mDialogView.animate()
.alpha(1)
- .translationY(0)
+ .translationX(0)
.setDuration(300)
.setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
.withEndAction(() -> {
@@ -205,20 +200,10 @@
VolumeUiLayout hardwareLayout = VolumeUiLayout.get(mDialogView);
hardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE));
- ViewGroup dialogContentView = mDialog.findViewById(R.id.volume_dialog_content);
- mDialogRowsView = dialogContentView.findViewById(R.id.volume_dialog_rows);
+ mDialogRowsView = mDialog.findViewById(R.id.volume_dialog_rows);
mRingerIcon = mDialog.findViewById(R.id.ringer_icon);
mRingerStatus = mDialog.findViewById(R.id.ringer_status);
- mExpanded = false;
- mExpandButton = mDialogView.findViewById(R.id.volume_expand_button);
- mExpandButton.setOnClickListener(mClickExpand);
- mExpandButton.setVisibility(
- AudioSystem.isSingleVolume(mContext) ? View.GONE : View.VISIBLE);
-
- mOutputChooser = mDialogView.findViewById(R.id.output_chooser);
- mOutputChooser.setOnClickListener(mClickOutputChooser);
-
if (mRows.isEmpty()) {
addRow(AudioManager.STREAM_MUSIC,
R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true);
@@ -239,6 +224,10 @@
} else {
addExistingRows();
}
+
+ mOutputChooser = mDialogView.findViewById(R.id.output_chooser);
+ mOutputChooser.setOnClickListener(mClickOutputChooser);
+
updateRowsH(getActiveRow());
initRingerH();
}
@@ -273,11 +262,9 @@
VolumeRow row = new VolumeRow();
initRow(row, stream, iconRes, iconMuteRes, important, defaultStream);
int rowSize;
- int viewSize;
- if (mShowA11yStream && dynamic && (rowSize = mRows.size()) > 1
- && (viewSize = mDialogRowsView.getChildCount()) > 1) {
- // A11y Stream should be the last in the list
- mDialogRowsView.addView(row.view, viewSize - 2);
+ if (mShowA11yStream && dynamic && (rowSize = mRows.size()) > 1) {
+ // A11y Stream should be the first in the list, so it's shown to start of other rows
+ mDialogRowsView.addView(row.view, 0);
mRows.add(rowSize - 2, row);
} else {
mDialogRowsView.addView(row.view);
@@ -315,7 +302,6 @@
public void dump(PrintWriter writer) {
writer.println(VolumeDialogImpl.class.getSimpleName() + " state:");
writer.print(" mShowing: "); writer.println(mShowing);
- writer.print(" mExpanded: "); writer.println(mExpanded);
writer.print(" mActiveStream: "); writer.println(mActiveStream);
writer.print(" mDynamic: "); writer.println(mDynamic);
writer.print(" mAutomute: "); writer.println(mAutomute);
@@ -432,6 +418,13 @@
}
updateRingerH();
});
+ mRingerIcon.setOnLongClickListener(v -> {
+ Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ dismissH(DISMISS_REASON_SETTINGS_CLICKED);
+ mContext.startActivity(intent);
+ return true;
+ });
updateRingerH();
}
@@ -468,7 +461,6 @@
private int computeTimeoutH() {
if (mAccessibility.mFeedbackEnabled) return 20000;
if (mHovering) return 16000;
- if (mExpanded) return 5000;
if (mSafetyWarning != null) return 5000;
return 3000;
}
@@ -480,13 +472,11 @@
mDialogView.animate().cancel();
mShowing = false;
- updateExpandedH(false /* expanding */, true /* dismissing */);
-
- mDialogView.setTranslationY(0);
+ mDialogView.setTranslationX(0);
mDialogView.setAlpha(1);
mDialogView.animate()
.alpha(0)
- .translationY(-mDialogView.getHeight())
+ .translationX(mDialogView.getWidth() / 2)
.setDuration(250)
.setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
.withEndAction(() -> mHandler.postDelayed(() -> {
@@ -514,67 +504,6 @@
}
}
- private void updateExpandedH(final boolean expanded, final boolean dismissing) {
- if (D.BUG) Log.d(TAG, "updateExpandedH " + expanded);
-
- if (mExpanded == expanded) return;
- mExpanded = expanded;
- mExpandButtonAnimationRunning = isAttached();
- updateExpandButtonH();
- TransitionManager.endTransitions(mDialogView);
- final VolumeRow activeRow = getActiveRow();
- if (!dismissing) {
- mWindow.setLayout(mWindow.getAttributes().width, ViewGroup.LayoutParams.MATCH_PARENT);
- TransitionManager.beginDelayedTransition(mDialogView, getTransition());
- }
- updateRowsH(activeRow);
- rescheduleTimeoutH();
- }
-
- private AutoTransition getTransition() {
- AutoTransition transition = new AutoTransition();
- transition.setDuration(300);
- transition.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- return transition;
- }
-
- private void updateExpandButtonH() {
- if (D.BUG) Log.d(TAG, "updateExpandButtonH");
-
- mExpandButton.setClickable(!mExpandButtonAnimationRunning);
- if (!(mExpandButtonAnimationRunning && isAttached())) {
- final int res = mExpanded ? R.drawable.ic_volume_collapse_animation
- : R.drawable.ic_volume_expand_animation;
- if (hasTouchFeature()) {
- mExpandButton.setImageResource(res);
- } else {
- // if there is no touch feature, show the volume ringer instead
- mExpandButton.setImageResource(R.drawable.ic_volume_ringer);
- mExpandButton.setBackgroundResource(0); // remove gray background emphasis
- }
- mExpandButton.setContentDescription(mContext.getString(mExpanded ?
- R.string.accessibility_volume_collapse : R.string.accessibility_volume_expand));
- }
- if (mExpandButtonAnimationRunning) {
- final Drawable d = mExpandButton.getDrawable();
- if (d instanceof AnimatedVectorDrawable) {
- // workaround to reset drawable
- final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) d.getConstantState()
- .newDrawable();
- mExpandButton.setImageDrawable(avd);
- avd.start();
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- mExpandButtonAnimationRunning = false;
- updateExpandButtonH();
- rescheduleTimeoutH();
- }
- }, 300);
- }
- }
- }
-
private boolean isAttached() {
return mDialogView != null && mDialogView.isAttachedToWindow();
}
@@ -597,7 +526,7 @@
return true;
}
- return row.defaultStream || isActive || (mExpanded && row.important);
+ return row.defaultStream || isActive;
}
private void updateRowsH(final VolumeRow activeRow) {
@@ -954,16 +883,6 @@
}
}
- private final OnClickListener mClickExpand = new OnClickListener() {
- @Override
- public void onClick(View v) {
- mExpandButton.animate().cancel();
- final boolean newExpand = !mExpanded;
- Events.writeEvent(mContext, Events.EVENT_EXPAND, newExpand);
- updateExpandedH(newExpand, false /* dismissing */);
- }
- };
-
private final OnClickListener mClickOutputChooser = new OnClickListener() {
@Override
public void onClick(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
index 49ac9b6..1c9cbc1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
@@ -14,15 +14,37 @@
package com.android.systemui.volume;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.content.Context;
+import android.content.res.Configuration;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+
+import com.android.systemui.R;
+import com.android.systemui.util.leak.RotationUtils;
public class VolumeUiLayout extends FrameLayout {
+ private View mChild;
+ private int mOldHeight;
+ private boolean mAnimating;
+ private AnimatorSet mAnimation;
+ private boolean mHasOutsideTouch;
+ private int mRotation = ROTATION_NONE;
public VolumeUiLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -40,11 +62,245 @@
}
@Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mChild == null) {
+ if (getChildCount() != 0) {
+ mChild = getChildAt(0);
+ mOldHeight = mChild.getMeasuredHeight();
+ updateRotation();
+ } else {
+ return;
+ }
+ }
+ int newHeight = mChild.getMeasuredHeight();
+ if (newHeight != mOldHeight) {
+ animateChild(mOldHeight, newHeight);
+ }
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ updateRotation();
+ }
+
+ private void updateRotation() {
+ int rotation = RotationUtils.getRotation(getContext());
+ if (rotation != mRotation) {
+ rotate(mRotation, rotation);
+ mRotation = rotation;
+ }
+ }
+
+ private void rotate(View view, int from, int to, boolean swapDimens) {
+ if (from != ROTATION_NONE && to != ROTATION_NONE) {
+ // Rather than handling this confusing case, just do 2 rotations.
+ rotate(view, from, ROTATION_NONE, swapDimens);
+ rotate(view, ROTATION_NONE, to, swapDimens);
+ return;
+ }
+ if (from == ROTATION_LANDSCAPE || to == ROTATION_SEASCAPE) {
+ rotateRight(view);
+ } else {
+ rotateLeft(view);
+ }
+ if (to != ROTATION_NONE) {
+ if (swapDimens && view instanceof LinearLayout) {
+ LinearLayout linearLayout = (LinearLayout) view;
+ linearLayout.setOrientation(LinearLayout.HORIZONTAL);
+ swapDimens(view);
+ }
+ } else {
+ if (swapDimens && view instanceof LinearLayout) {
+ LinearLayout linearLayout = (LinearLayout) view;
+ linearLayout.setOrientation(LinearLayout.VERTICAL);
+ swapDimens(view);
+ }
+ }
+ }
+
+ private void rotate(int from, int to) {
+ View footer = mChild.findViewById(R.id.footer);
+ rotate(footer, from, to, false);
+ rotate(this, from, to, true);
+ rotate(mChild, from, to, true);
+ ViewGroup rows = mChild.findViewById(R.id.volume_dialog_rows);
+ rotate(rows, from, to, true);
+ int rowCount = rows.getChildCount();
+ for (int i = 0; i < rowCount; i++) {
+ View child = rows.getChildAt(i);
+ if (to == ROTATION_SEASCAPE) {
+ rotateSeekBars(to, 0);
+ } else if (to == ROTATION_LANDSCAPE) {
+ rotateSeekBars(to, 180);
+ } else {
+ rotateSeekBars(to, 270);
+ }
+ rotate(child, from, to, true);
+ }
+ }
+
+ private void swapDimens(View v) {
+ if (v == null) {
+ return;
+ }
+ ViewGroup.LayoutParams params = v.getLayoutParams();
+ int h = params.width;
+ params.width = params.height;
+ params.height = h;
+ v.setLayoutParams(params);
+ }
+
+ private void rotateSeekBars(int to, int rotation) {
+ SeekBar seekbar = mChild.findViewById(R.id.volume_row_slider);
+ if (seekbar != null) {
+ seekbar.setRotation((float) rotation);
+ }
+
+ View parent = mChild.findViewById(R.id.volume_row_slider_frame);
+ swapDimens(parent);
+ ViewGroup.LayoutParams params = seekbar.getLayoutParams();
+ ViewGroup.LayoutParams parentParams = parent.getLayoutParams();
+ if (to != ROTATION_NONE) {
+ params.height = parentParams.height;
+ params.width = parentParams.width;
+ } else {
+ params.height = parentParams.width;
+ params.width = parentParams.height;
+ }
+ seekbar.setLayoutParams(params);
+ }
+
+ private int rotateGravityRight(int gravity) {
+ int retGravity = 0;
+ int layoutDirection = getLayoutDirection();
+ final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
+ final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
+
+ switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.CENTER_HORIZONTAL:
+ retGravity |= Gravity.CENTER_VERTICAL;
+ break;
+ case Gravity.RIGHT:
+ retGravity |= Gravity.BOTTOM;
+ break;
+ case Gravity.LEFT:
+ default:
+ retGravity |= Gravity.TOP;
+ break;
+ }
+
+ switch (verticalGravity) {
+ case Gravity.CENTER_VERTICAL:
+ retGravity |= Gravity.CENTER_HORIZONTAL;
+ break;
+ case Gravity.BOTTOM:
+ retGravity |= Gravity.LEFT;
+ break;
+ case Gravity.TOP:
+ default:
+ retGravity |= Gravity.RIGHT;
+ break;
+ }
+ return retGravity;
+ }
+
+ private int rotateGravityLeft(int gravity) {
+ if (gravity == -1) {
+ gravity = Gravity.TOP | Gravity.START;
+ }
+ int retGravity = 0;
+ int layoutDirection = getLayoutDirection();
+ final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
+ final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
+
+ switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.CENTER_HORIZONTAL:
+ retGravity |= Gravity.CENTER_VERTICAL;
+ break;
+ case Gravity.RIGHT:
+ retGravity |= Gravity.TOP;
+ break;
+ case Gravity.LEFT:
+ default:
+ retGravity |= Gravity.BOTTOM;
+ break;
+ }
+
+ switch (verticalGravity) {
+ case Gravity.CENTER_VERTICAL:
+ retGravity |= Gravity.CENTER_HORIZONTAL;
+ break;
+ case Gravity.BOTTOM:
+ retGravity |= Gravity.RIGHT;
+ break;
+ case Gravity.TOP:
+ default:
+ retGravity |= Gravity.LEFT;
+ break;
+ }
+ return retGravity;
+ }
+
+ private void rotateLeft(View v) {
+ if (v.getParent() instanceof FrameLayout) {
+ LayoutParams p = (LayoutParams) v.getLayoutParams();
+ p.gravity = rotateGravityLeft(p.gravity);
+ }
+
+ v.setPadding(v.getPaddingTop(), v.getPaddingRight(), v.getPaddingBottom(),
+ v.getPaddingLeft());
+ MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams();
+ params.setMargins(params.topMargin, params.rightMargin, params.bottomMargin,
+ params.leftMargin);
+ v.setLayoutParams(params);
+ }
+
+ private void rotateRight(View v) {
+ if (v.getParent() instanceof FrameLayout) {
+ LayoutParams p = (LayoutParams) v.getLayoutParams();
+ p.gravity = rotateGravityRight(p.gravity);
+ }
+
+ v.setPadding(v.getPaddingBottom(), v.getPaddingLeft(), v.getPaddingTop(),
+ v.getPaddingRight());
+ MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams();
+ params.setMargins(params.bottomMargin, params.leftMargin, params.topMargin,
+ params.rightMargin);
+ v.setLayoutParams(params);
+ }
+
+ private void animateChild(int oldHeight, int newHeight) {
+ if (true) return;
+ if (mAnimating) {
+ mAnimation.cancel();
+ }
+ mAnimating = true;
+ mAnimation = new AnimatorSet();
+ mAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimating = false;
+ }
+ });
+ int fromTop = mChild.getTop();
+ int fromBottom = mChild.getBottom();
+ int toTop = fromTop - ((newHeight - oldHeight) / 2);
+ int toBottom = fromBottom + ((newHeight - oldHeight) / 2);
+ ObjectAnimator top = ObjectAnimator.ofInt(mChild, "top", fromTop, toTop);
+ mAnimation.playTogether(top,
+ ObjectAnimator.ofInt(mChild, "bottom", fromBottom, toBottom));
+ }
+
+
+ @Override
public ViewOutlineProvider getOutlineProvider() {
return super.getOutlineProvider();
}
public void setOutsideTouchListener(OnClickListener onClickListener) {
+ mHasOutsideTouch = true;
requestLayout();
setOnClickListener(onClickListener);
setClickable(true);
@@ -60,7 +316,14 @@
}
private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener = inoutInfo -> {
+ if (mHasOutsideTouch || (mChild == null)) {
+ inoutInfo.setTouchableInsets(
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+ return;
+ }
inoutInfo.setTouchableInsets(
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT);
+ inoutInfo.contentInsets.set(mChild.getLeft(), mChild.getTop(),
+ 0, getBottom() - mChild.getBottom());
};
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
index 0a68389..f9ec3f92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
@@ -32,11 +32,14 @@
import android.app.ActivityManager;
import android.app.Notification;
+import android.app.NotificationManager;
import android.content.Context;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationRankingUpdate;
import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -120,6 +123,23 @@
}
}
+ private void setUserSentiment(String key, int sentiment) {
+ doAnswer(invocationOnMock -> {
+ NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
+ invocationOnMock.getArguments()[1];
+ ranking.populate(
+ key,
+ 0,
+ false,
+ 0,
+ 0,
+ NotificationManager.IMPORTANCE_DEFAULT,
+ null, null,
+ null, null, null, true, sentiment);
+ return true;
+ }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
+ }
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -158,6 +178,8 @@
mEntryManager = new TestableNotificationEntryManager(mContext, mBarService);
mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mCallback, mHeadsUpManager);
+
+ setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
}
@Test
@@ -196,6 +218,8 @@
assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
assertNotNull(entry.row);
+ assertEquals(mEntry.userSentiment,
+ NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
}
@Test
@@ -204,6 +228,8 @@
mEntryManager.getNotificationData().add(mEntry);
+ setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+
mHandler.post(() -> {
mEntryManager.updateNotification(mSbn, mRankingMap);
});
@@ -219,6 +245,8 @@
verify(mForegroundServiceController).updateNotification(eq(mSbn), anyInt());
verify(mCallback).onNotificationUpdated(mSbn);
assertNotNull(mEntry.row);
+ assertEquals(mEntry.userSentiment,
+ NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
}
@Test
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index f33ec55..5188910 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -3140,6 +3140,7 @@
mCurrentToken = 0;
Slog.w(TAG, "Transport " + newTransportName + " not available: current token = 0");
}
+ mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
} else {
Slog.w(TAG, "Transport " + newTransportName + " not registered: current token = 0");
// The named transport isn't registered, so we can't know what its current dataset token
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index a628c9d..540f5a1 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -177,12 +177,15 @@
}
}
+ // IBackupManager binder API
+
/**
* Querying activity state of backup service. Calling this method before initialize yields
* undefined result.
* @param userHandle The user in which the activity state of backup service is queried.
* @return true if the service is active.
*/
+ @Override
public boolean isBackupServiceActive(final int userHandle) {
// TODO: http://b/22388012
if (userHandle == UserHandle.USER_SYSTEM) {
@@ -193,7 +196,6 @@
return false;
}
- // IBackupManager binder API
@Override
public void dataChanged(String packageName) throws RemoteException {
BackupManagerServiceInterface svc = mService;
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index 09456b68..30fd25a 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -29,14 +29,12 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
-import android.util.EventLog;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.util.Preconditions;
-import com.android.server.EventLogTags;
import com.android.server.backup.transport.OnTransportRegisteredListener;
import com.android.server.backup.transport.TransportClient;
import com.android.server.backup.transport.TransportClientManager;
@@ -574,8 +572,6 @@
return BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
}
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 1);
-
int result;
try {
String transportName = transport.name();
@@ -587,7 +583,6 @@
result = BackupManager.SUCCESS;
} catch (RemoteException e) {
Slog.e(TAG, "Transport " + transportString + " died while registering");
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 0);
result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
}
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClient.java b/services/backup/java/com/android/server/backup/transport/TransportClient.java
index bd4a0bb..399f338 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClient.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportClient.java
@@ -29,12 +29,14 @@
import android.os.Looper;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.EventLog;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.util.Preconditions;
+import com.android.server.EventLogTags;
import com.android.server.backup.TransportManager;
import java.lang.annotation.Retention;
@@ -419,10 +421,45 @@
@GuardedBy("mStateLock")
private void setStateLocked(@State int state, @Nullable IBackupTransport transport) {
log(Log.VERBOSE, "State: " + stateToString(mState) + " => " + stateToString(state));
+ onStateTransition(mState, state);
mState = state;
mTransport = transport;
}
+ private void onStateTransition(int oldState, int newState) {
+ String transport = mTransportComponent.flattenToShortString();
+ int bound = transitionThroughState(oldState, newState, State.BOUND_AND_CONNECTING);
+ int connected = transitionThroughState(oldState, newState, State.CONNECTED);
+ if (bound != Transition.NO_TRANSITION) {
+ int value = (bound == Transition.UP) ? 1 : 0; // 1 is bound, 0 is not bound
+ EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transport, value);
+ }
+ if (connected != Transition.NO_TRANSITION) {
+ int value = (connected == Transition.UP) ? 1 : 0; // 1 is connected, 0 is not connected
+ EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_CONNECTION, transport, value);
+ }
+ }
+
+ /**
+ * Returns:
+ *
+ * <ul>
+ * <li>{@link Transition#UP}, if oldState < stateReference <= newState
+ * <li>{@link Transition#DOWN}, if oldState >= stateReference > newState
+ * <li>{@link Transition#NO_TRANSITION}, otherwise
+ */
+ @Transition
+ private int transitionThroughState(
+ @State int oldState, @State int newState, @State int stateReference) {
+ if (oldState < stateReference && stateReference <= newState) {
+ return Transition.UP;
+ }
+ if (oldState >= stateReference && stateReference > newState) {
+ return Transition.DOWN;
+ }
+ return Transition.NO_TRANSITION;
+ }
+
@GuardedBy("mStateLock")
private void checkStateIntegrityLocked() {
switch (mState) {
@@ -481,6 +518,14 @@
// CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis());
}
+ @IntDef({Transition.DOWN, Transition.NO_TRANSITION, Transition.UP})
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface Transition {
+ int DOWN = -1;
+ int NO_TRANSITION = 0;
+ int UP = 1;
+ }
+
@IntDef({State.UNUSABLE, State.IDLE, State.BOUND_AND_CONNECTING, State.CONNECTED})
@Retention(RetentionPolicy.SOURCE)
private @interface State {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index d9713a5..337406d 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -60,6 +60,7 @@
import android.provider.Settings.SettingNotFoundException;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.util.DumpUtils;
import com.android.server.pm.UserRestrictionsUtils;
@@ -415,9 +416,14 @@
int systemUiUid = -1;
try {
- systemUiUid = mContext.getPackageManager()
- .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
- UserHandle.USER_SYSTEM);
+ // Check if device is configured with no home screen, which implies no SystemUI.
+ boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen);
+ if (!noHome) {
+ systemUiUid = mContext.getPackageManager()
+ .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
+ UserHandle.USER_SYSTEM);
+ }
+ Slog.d(TAG, "Detected SystemUiUid: " + Integer.toString(systemUiUid));
} catch (PackageManager.NameNotFoundException e) {
// Some platforms, such as wearables do not have a system ui.
Slog.w(TAG, "Unable to resolve SystemUI's UID.", e);
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 8361132..732ac66 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -133,6 +133,7 @@
2846 full_backup_cancelled (Package|3),(Message|3)
2850 backup_transport_lifecycle (Transport|3),(Bound|1|1)
+2851 backup_transport_connection (Transport|3),(Connected|1|1)
# ---------------------------
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
index 8776f3a..782d4dd 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -26,6 +26,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -89,6 +91,9 @@
private final MyHandler mHandler;
+ @VisibleForTesting
+ FeatureFlagsObserver mFlagsObserver;
+
/**
* Pair of (uid (not user-id), packageName) with OP_RUN_ANY_IN_BACKGROUND *not* allowed.
*/
@@ -111,13 +116,32 @@
boolean mStarted;
@GuardedBy("mLock")
- boolean mForceAllAppsStandby; // True if device is in extreme battery saver mode
+ boolean mIsCharging;
@GuardedBy("mLock")
- boolean mForcedAppStandbyEnabled; // True if the forced app standby feature is enabled
+ boolean mBatterySaverEnabled;
- private class FeatureFlagObserver extends ContentObserver {
- FeatureFlagObserver() {
+ /**
+ * True if the forced app standby is currently enabled
+ */
+ @GuardedBy("mLock")
+ boolean mForceAllAppsStandby;
+
+ /**
+ * True if the forced app standby for small battery devices feature is enabled in settings
+ */
+ @GuardedBy("mLock")
+ boolean mForceAllAppStandbyForSmallBattery;
+
+ /**
+ * True if the forced app standby feature is enabled in settings
+ */
+ @GuardedBy("mLock")
+ boolean mForcedAppStandbyEnabled;
+
+ @VisibleForTesting
+ class FeatureFlagsObserver extends ContentObserver {
+ FeatureFlagsObserver() {
super(null);
}
@@ -125,6 +149,9 @@
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED),
false, this);
+
+ mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED), false, this);
}
boolean isForcedAppStandbyEnabled() {
@@ -132,20 +159,43 @@
Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1;
}
+ boolean isForcedAppStandbyForSmallBatteryEnabled() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 0) == 1;
+ }
+
@Override
- public void onChange(boolean selfChange) {
- final boolean enabled = isForcedAppStandbyEnabled();
- synchronized (mLock) {
- if (mForcedAppStandbyEnabled == enabled) {
- return;
+ public void onChange(boolean selfChange, Uri uri) {
+ if (Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED).equals(uri)) {
+ final boolean enabled = isForcedAppStandbyEnabled();
+ synchronized (mLock) {
+ if (mForcedAppStandbyEnabled == enabled) {
+ return;
+ }
+ mForcedAppStandbyEnabled = enabled;
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Forced app standby feature flag changed: " + mForcedAppStandbyEnabled);
+ }
}
- mForcedAppStandbyEnabled = enabled;
- if (DEBUG) {
- Slog.d(TAG,
- "Forced app standby feature flag changed: " + mForcedAppStandbyEnabled);
+ mHandler.notifyForcedAppStandbyFeatureFlagChanged();
+ } else if (Settings.Global.getUriFor(
+ Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED).equals(uri)) {
+ final boolean enabled = isForcedAppStandbyForSmallBatteryEnabled();
+ synchronized (mLock) {
+ if (mForceAllAppStandbyForSmallBattery == enabled) {
+ return;
+ }
+ mForceAllAppStandbyForSmallBattery = enabled;
+ if (DEBUG) {
+ Slog.d(TAG, "Forced app standby for small battery feature flag changed: "
+ + mForceAllAppStandbyForSmallBattery);
+ }
+ updateForceAllAppStandbyState();
}
+ } else {
+ Slog.w(TAG, "Unexpected feature flag uri encountered: " + uri);
}
- mHandler.notifyFeatureFlagChanged();
}
}
@@ -286,9 +336,11 @@
mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
- final FeatureFlagObserver flagObserver = new FeatureFlagObserver();
- flagObserver.register();
- mForcedAppStandbyEnabled = flagObserver.isForcedAppStandbyEnabled();
+ mFlagsObserver = new FeatureFlagsObserver();
+ mFlagsObserver.register();
+ mForcedAppStandbyEnabled = mFlagsObserver.isForcedAppStandbyEnabled();
+ mForceAllAppStandbyForSmallBattery =
+ mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled();
try {
mIActivityManager.registerUidObserver(new UidObserver(),
@@ -303,16 +355,24 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_REMOVED);
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
mContext.registerReceiver(new MyReceiver(), filter);
refreshForcedAppStandbyUidPackagesLocked();
mPowerManagerInternal.registerLowPowerModeObserver(
ServiceType.FORCE_ALL_APPS_STANDBY,
- (state) -> updateForceAllAppsStandby(state.batterySaverEnabled));
+ (state) -> {
+ synchronized (mLock) {
+ mBatterySaverEnabled = state.batterySaverEnabled;
+ updateForceAllAppStandbyState();
+ }
+ });
- updateForceAllAppsStandby(mPowerManagerInternal.getLowPowerState(
- ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled);
+ mBatterySaverEnabled = mPowerManagerInternal.getLowPowerState(
+ ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled;
+
+ updateForceAllAppStandbyState();
}
}
@@ -337,6 +397,11 @@
return LocalServices.getService(PowerManagerInternal.class);
}
+ @VisibleForTesting
+ boolean isSmallBatteryDevice() {
+ return ActivityManager.isSmallBatteryDevice();
+ }
+
/**
* Update {@link #mRunAnyRestrictedPackages} with the current app ops state.
*/
@@ -366,18 +431,29 @@
}
}
+ private void updateForceAllAppStandbyState() {
+ synchronized (mLock) {
+ if (mIsCharging) {
+ toggleForceAllAppsStandbyLocked(false);
+ } else if (mForceAllAppStandbyForSmallBattery
+ && isSmallBatteryDevice()) {
+ toggleForceAllAppsStandbyLocked(true);
+ } else {
+ toggleForceAllAppsStandbyLocked(mBatterySaverEnabled);
+ }
+ }
+ }
+
/**
* Update {@link #mForceAllAppsStandby} and notifies the listeners.
*/
- void updateForceAllAppsStandby(boolean enable) {
- synchronized (mLock) {
- if (enable == mForceAllAppsStandby) {
- return;
- }
- mForceAllAppsStandby = enable;
-
- mHandler.notifyForceAllAppsStandbyChanged();
+ private void toggleForceAllAppsStandbyLocked(boolean enable) {
+ if (enable == mForceAllAppsStandby) {
+ return;
}
+ mForceAllAppsStandby = enable;
+
+ mHandler.notifyForceAllAppsStandbyChanged();
}
private int findForcedAppStandbyUidPackageIndexLocked(int uid, @NonNull String packageName) {
@@ -512,6 +588,13 @@
if (userId > 0) {
mHandler.doUserRemoved(userId);
}
+ } else if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
+ int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
+ synchronized (mLock) {
+ mIsCharging = (status == BatteryManager.BATTERY_STATUS_CHARGING
+ || status == BatteryManager.BATTERY_STATUS_FULL);
+ }
+ updateForceAllAppStandbyState();
}
}
}
@@ -530,7 +613,7 @@
private static final int MSG_TEMP_WHITELIST_CHANGED = 5;
private static final int MSG_FORCE_ALL_CHANGED = 6;
private static final int MSG_USER_REMOVED = 7;
- private static final int MSG_FEATURE_FLAG_CHANGED = 8;
+ private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
public MyHandler(Looper looper) {
super(looper);
@@ -560,8 +643,8 @@
obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget();
}
- public void notifyFeatureFlagChanged() {
- obtainMessage(MSG_FEATURE_FLAG_CHANGED).sendToTarget();
+ public void notifyForcedAppStandbyFeatureFlagChanged() {
+ obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget();
}
public void doUserRemoved(int userId) {
@@ -615,7 +698,7 @@
l.onForceAllAppsStandbyChanged(sender);
}
return;
- case MSG_FEATURE_FLAG_CHANGED:
+ case MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED:
// Feature flag for forced app standby changed.
final boolean unblockAlarms;
synchronized (mLock) {
@@ -839,6 +922,18 @@
pw.println(isForceAllAppsStandbyEnabled());
pw.print(indent);
+ pw.print("Small Battery Device: ");
+ pw.println(isSmallBatteryDevice());
+
+ pw.print(indent);
+ pw.print("Force all apps standby for small battery device: ");
+ pw.println(mForceAllAppStandbyForSmallBattery);
+
+ pw.print(indent);
+ pw.print("Charging: ");
+ pw.println(mIsCharging);
+
+ pw.print(indent);
pw.print("Foreground uids: [");
String sep = "";
@@ -877,6 +972,11 @@
final long token = proto.start(fieldId);
proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY, mForceAllAppsStandby);
+ proto.write(ForceAppStandbyTrackerProto.IS_SMALL_BATTERY_DEVICE,
+ isSmallBatteryDevice());
+ proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY_FOR_SMALL_BATTERY,
+ mForceAllAppStandbyForSmallBattery);
+ proto.write(ForceAppStandbyTrackerProto.IS_CHARGING, mIsCharging);
for (int i = 0; i < mForegroundUids.size(); i++) {
if (mForegroundUids.valueAt(i)) {
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 9d228c3..46a35ec 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1235,8 +1235,8 @@
* reserved for future improved input validation.
*/
@Override
- public synchronized void removeTransportModeTransforms(
- ParcelFileDescriptor socket, int resourceId) throws RemoteException {
+ public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
+ throws RemoteException {
try {
mSrvConfig
.getNetdInstance()
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index dfe89e0..8cff20c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13674,7 +13674,8 @@
* not.
*/
private void enforceSystemHasVrFeature() {
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
throw new UnsupportedOperationException("VR mode not supported on this device!");
}
}
@@ -13733,9 +13734,7 @@
@Override
public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
- throw new UnsupportedOperationException("VR mode not supported on this device!");
- }
+ enforceSystemHasVrFeature();
final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
@@ -13767,9 +13766,7 @@
@Override
public boolean isVrModePackageEnabled(ComponentName packageName) {
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
- throw new UnsupportedOperationException("VR mode not supported on this device!");
- }
+ enforceSystemHasVrFeature();
final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index b5fbee6..ceec97c 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -21,6 +21,7 @@
import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
import static android.app.ActivityOptions.ANIM_SCALE_UP;
import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
@@ -1481,6 +1482,10 @@
case ANIM_OPEN_CROSS_PROFILE_APPS:
service.mWindowManager.overridePendingAppTransitionStartCrossProfileApps();
break;
+ case ANIM_REMOTE_ANIMATION:
+ service.mWindowManager.overridePendingAppTransitionRemote(
+ pendingOptions.getRemoteAnimationAdapter());
+ break;
default:
Slog.e(TAG, "applyOptionsLocked: Unknown animationType=" + animationType);
break;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index b567303..16c2d2d 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import static android.Manifest.permission.ACTIVITY_EMBEDDING;
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.START_ANY_ACTIVITY;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
@@ -162,6 +163,7 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+import android.view.RemoteAnimationAdapter;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.ReferrerIntent;
@@ -1680,6 +1682,18 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
+
+ // Check permission for remote animations
+ final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
+ if (adapter != null && mService.checkPermission(
+ CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
+ != PERMISSION_GRANTED) {
+ final String msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") with remoteAnimationAdapter";
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
}
return true;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 02e4fe0..a55fec5 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2009,5 +2009,14 @@
mDisplayPowerController.persistBrightnessSliderEvents();
}
}
+
+ @Override
+ public void onOverlayChanged() {
+ synchronized (mSyncRoot) {
+ if (updateLogicalDisplaysLocked()) {
+ scheduleTraversalLocked(false);
+ }
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ee08c38..879c024 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -63,7 +63,6 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
-import android.os.ServiceSpecificException;
import android.os.ShellCallback;
import android.os.StrictMode;
import android.os.SystemProperties;
@@ -78,11 +77,10 @@
import android.security.keystore.AndroidKeyStoreProvider;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
+import android.security.keystore.KeychainProtectionParameter;
import android.security.keystore.UserNotAuthenticatedException;
-import android.security.keystore.EntryRecoveryData;
-import android.security.keystore.RecoveryData;
-import android.security.keystore.RecoveryMetadata;
-import android.security.keystore.RecoveryManagerException;
+import android.security.keystore.WrappedApplicationKey;
+import android.security.keystore.KeychainSnapshot;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.text.TextUtils;
@@ -90,6 +88,7 @@
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -1874,6 +1873,7 @@
mSpManager.removeUser(userId);
mStorage.removeUser(userId);
mStrongAuth.removeUser(userId);
+ cleanSpCache();
final KeyStore ks = KeyStore.getInstance();
ks.onUserRemoved(userId);
@@ -1968,7 +1968,7 @@
}
@Override
- public RecoveryData getRecoveryData(@NonNull byte[] account) throws RemoteException {
+ public KeychainSnapshot getRecoveryData(@NonNull byte[] account) throws RemoteException {
return mRecoverableKeyStoreManager.getRecoveryData(account);
}
@@ -1997,7 +1997,7 @@
}
@Override
- public void setRecoverySecretTypes(@NonNull @RecoveryMetadata.UserSecretType
+ public void setRecoverySecretTypes(@NonNull @KeychainProtectionParameter.UserSecretType
int[] secretTypes) throws RemoteException {
mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
}
@@ -2014,7 +2014,7 @@
}
@Override
- public void recoverySecretAvailable(@NonNull RecoveryMetadata recoverySecret)
+ public void recoverySecretAvailable(@NonNull KeychainProtectionParameter recoverySecret)
throws RemoteException {
mRecoverableKeyStoreManager.recoverySecretAvailable(recoverySecret);
}
@@ -2022,7 +2022,7 @@
@Override
public byte[] startRecoverySession(@NonNull String sessionId,
@NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams,
- @NonNull byte[] vaultChallenge, @NonNull List<RecoveryMetadata> secrets)
+ @NonNull byte[] vaultChallenge, @NonNull List<KeychainProtectionParameter> secrets)
throws RemoteException {
return mRecoverableKeyStoreManager.startRecoverySession(sessionId, verifierPublicKey,
vaultParams, vaultChallenge, secrets);
@@ -2030,7 +2030,7 @@
@Override
public Map<String, byte[]> recoverKeys(@NonNull String sessionId,
- @NonNull byte[] recoveryKeyBlob, @NonNull List<EntryRecoveryData> applicationKeys)
+ @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys)
throws RemoteException {
return mRecoverableKeyStoreManager.recoverKeys(
sessionId, recoveryKeyBlob, applicationKeys);
@@ -2112,6 +2112,63 @@
}
/**
+ * A user's synthetic password does not change so it must be cached in certain circumstances to
+ * enable untrusted credential reset.
+ *
+ * Untrusted credential reset will be removed in a future version (b/68036371) at which point
+ * this cache is no longer needed as the SP will always be known when changing the user's
+ * credential.
+ */
+ @GuardedBy("mSpManager")
+ private SparseArray<AuthenticationToken> mSpCache = new SparseArray();
+
+ private void onAuthTokenKnownForUser(@UserIdInt int userId, AuthenticationToken auth) {
+ // Update the SP cache, removing the entry when allowed
+ synchronized (mSpManager) {
+ if (shouldCacheSpForUser(userId)) {
+ Slog.i(TAG, "Caching SP for user " + userId);
+ mSpCache.put(userId, auth);
+ } else {
+ Slog.i(TAG, "Not caching SP for user " + userId);
+ mSpCache.delete(userId);
+ }
+ }
+ }
+
+ /** Clean up the SP cache by removing unneeded entries. */
+ private void cleanSpCache() {
+ synchronized (mSpManager) {
+ // Preserve indicies after removal by iterating backwards
+ for (int i = mSpCache.size() - 1; i >= 0; --i) {
+ final int userId = mSpCache.keyAt(i);
+ if (!shouldCacheSpForUser(userId)) {
+ Slog.i(TAG, "Uncaching SP for user " + userId);
+ mSpCache.removeAt(i);
+ }
+ }
+ }
+ }
+
+ private boolean shouldCacheSpForUser(@UserIdInt int userId) {
+ // Before the user setup has completed, an admin could be installed that requires the SP to
+ // be cached (see below).
+ if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0, userId) == 0) {
+ return true;
+ }
+
+ // If the user has an admin which can perform an untrusted credential reset, the SP needs to
+ // be cached. If there isn't a DevicePolicyManager then there can't be an admin in the first
+ // place so caching is not necessary.
+ final DevicePolicyManagerInternal dpmi = LocalServices.getService(
+ DevicePolicyManagerInternal.class);
+ if (dpmi == null) {
+ return false;
+ }
+ return dpmi.canUserHaveUntrustedCredentialReset(userId);
+ }
+
+ /**
* Precondition: vold and keystore unlocked.
*
* Create new synthetic password, set up synthetic password blob protected by the supplied
@@ -2126,9 +2183,7 @@
* 3. Once a user is migrated to have synthetic password, its value will never change, no matter
* whether the user changes his lockscreen PIN or clear/reset it. When the user clears its
* lockscreen PIN, we still maintain the existing synthetic password in a password blob
- * protected by a default PIN. The only exception is when the DPC performs an untrusted
- * credential change, in which case we have no way to derive the existing synthetic password
- * and has to create a new one.
+ * protected by a default PIN.
* 4. The user SID is linked with synthetic password, but its cleared/re-created when the user
* clears/re-creates his lockscreen PIN.
*
@@ -2148,13 +2203,23 @@
* This is the untrusted credential reset, OR the user sets a new lockscreen password
* FOR THE FIRST TIME on a SP-enabled device. New credential and new SID will be created
*/
+ @GuardedBy("mSpManager")
@VisibleForTesting
protected AuthenticationToken initializeSyntheticPasswordLocked(byte[] credentialHash,
String credential, int credentialType, int requestedQuality,
int userId) throws RemoteException {
Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId);
- AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid(getGateKeeperService(),
- credentialHash, credential, userId);
+ // Load from the cache or a make a new one
+ AuthenticationToken auth = mSpCache.get(userId);
+ if (auth != null) {
+ // If the synthetic password has been cached, we can only be in case 3., described
+ // above, for an untrusted credential reset so a new SID is still needed.
+ mSpManager.newSidForUser(getGateKeeperService(), auth, userId);
+ } else {
+ auth = mSpManager.newSyntheticPasswordAndSid(getGateKeeperService(),
+ credentialHash, credential, userId);
+ }
+ onAuthTokenKnownForUser(userId, auth);
if (auth == null) {
Slog.wtf(TAG, "initializeSyntheticPasswordLocked returns null auth token");
return null;
@@ -2269,6 +2334,8 @@
trustManager.setDeviceLockedForUser(userId, false);
}
mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
+
+ onAuthTokenKnownForUser(userId, authResult.authToken);
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
if (response.getTimeout() > 0) {
requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
@@ -2287,6 +2354,7 @@
* SID is gone. We also clear password from (software-based) keystore and vold, which will be
* added back when new password is set in future.
*/
+ @GuardedBy("mSpManager")
private long setLockCredentialWithAuthTokenLocked(String credential, int credentialType,
AuthenticationToken auth, int requestedQuality, int userId) throws RemoteException {
if (DEBUG) Slog.d(TAG, "setLockCredentialWithAuthTokenLocked: user=" + userId);
@@ -2334,6 +2402,7 @@
return newHandle;
}
+ @GuardedBy("mSpManager")
private void spBasedSetLockCredentialInternalLocked(String credential, int credentialType,
String savedCredential, int requestedQuality, int userId) throws RemoteException {
if (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId);
@@ -2369,13 +2438,19 @@
setLockCredentialWithAuthTokenLocked(credential, credentialType, auth, requestedQuality,
userId);
mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);
+ onAuthTokenKnownForUser(userId, auth);
} else if (response != null
- && response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR){
+ && response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR) {
// We are performing an untrusted credential change i.e. by DevicePolicyManager.
// So provision a new SP and SID. This would invalidate existing escrow tokens.
// Still support this for now but this flow will be removed in the next release.
-
Slog.w(TAG, "Untrusted credential change invoked");
+
+ if (mSpCache.get(userId) == null) {
+ throw new IllegalStateException(
+ "Untrusted credential reset not possible without cached SP");
+ }
+
initializeSyntheticPasswordLocked(null, credential, credentialType, requestedQuality,
userId);
synchronizeUnifiedWorkChallengeForProfiles(userId, null);
@@ -2486,8 +2561,9 @@
private boolean setLockCredentialWithTokenInternal(String credential, int type,
long tokenHandle, byte[] token, int requestedQuality, int userId) throws RemoteException {
+ final AuthenticationResult result;
synchronized (mSpManager) {
- AuthenticationResult result = mSpManager.unwrapTokenBasedSyntheticPassword(
+ result = mSpManager.unwrapTokenBasedSyntheticPassword(
getGateKeeperService(), tokenHandle, token, userId);
if (result.authToken == null) {
Slog.w(TAG, "Invalid escrow token supplied");
@@ -2508,8 +2584,9 @@
setLockCredentialWithAuthTokenLocked(credential, type, result.authToken,
requestedQuality, userId);
mSpManager.destroyPasswordBasedSyntheticPassword(oldHandle, userId);
- return true;
}
+ onAuthTokenKnownForUser(userId, result.authToken);
+ return true;
}
@Override
@@ -2529,6 +2606,7 @@
}
}
unlockUser(userId, null, authResult.authToken.deriveDiskEncryptionKey());
+ onAuthTokenKnownForUser(userId, authResult.authToken);
}
@Override
@@ -2610,6 +2688,8 @@
private class DeviceProvisionedObserver extends ContentObserver {
private final Uri mDeviceProvisionedUri = Settings.Global.getUriFor(
Settings.Global.DEVICE_PROVISIONED);
+ private final Uri mUserSetupCompleteUri = Settings.Secure.getUriFor(
+ Settings.Secure.USER_SETUP_COMPLETE);
private boolean mRegistered;
@@ -2627,6 +2707,8 @@
reportDeviceSetupComplete();
clearFrpCredentialIfOwnerNotSecure();
}
+ } else if (mUserSetupCompleteUri.equals(uri)) {
+ cleanSpCache();
}
}
@@ -2678,6 +2760,8 @@
if (register) {
mContext.getContentResolver().registerContentObserver(mDeviceProvisionedUri,
false, this);
+ mContext.getContentResolver().registerContentObserver(mUserSetupCompleteUri,
+ false, this, UserHandle.USER_ALL);
} else {
mContext.getContentResolver().unregisterContentObserver(this);
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index 5fe11b1..38745f6 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -16,15 +16,14 @@
package com.android.server.locksettings.recoverablekeystore;
-import static android.security.keystore.RecoveryMetadata.TYPE_LOCKSCREEN;
+import static android.security.keystore.KeychainProtectionParameter.TYPE_LOCKSCREEN;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.security.keystore.KeyDerivationParams;
-import android.security.keystore.EntryRecoveryData;
-import android.security.keystore.RecoveryData;
-import android.security.keystore.RecoveryMetadata;
+import android.security.keystore.KeychainProtectionParameter;
+import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.WrappedApplicationKey;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -251,12 +250,12 @@
}
// TODO: store raw data in RecoveryServiceMetadataEntry and generate Parcelables later
// TODO: use Builder.
- RecoveryMetadata metadata = new RecoveryMetadata(
+ KeychainProtectionParameter metadata = new KeychainProtectionParameter(
/*userSecretType=*/ TYPE_LOCKSCREEN,
/*lockScreenUiFormat=*/ getUiFormat(mCredentialType, mCredential),
/*keyDerivationParams=*/ KeyDerivationParams.createSha256Params(salt),
/*secret=*/ new byte[0]);
- ArrayList<RecoveryMetadata> metadataList = new ArrayList<>();
+ ArrayList<KeychainProtectionParameter> metadataList = new ArrayList<>();
metadataList.add(metadata);
int snapshotVersion = incrementSnapshotVersion(recoveryAgentUid);
@@ -265,7 +264,7 @@
mRecoverableKeyStoreDb.setShouldCreateSnapshot(mUserId, recoveryAgentUid, false);
// TODO: use Builder.
- mRecoverySnapshotStorage.put(recoveryAgentUid, new RecoveryData(
+ mRecoverySnapshotStorage.put(recoveryAgentUid, new KeychainSnapshot(
snapshotVersion,
/*recoveryMetadata=*/ metadataList,
/*applicationKeyBlobs=*/ createApplicationKeyEntries(encryptedApplicationKeys),
@@ -308,7 +307,7 @@
*/
private boolean shoudCreateSnapshot(int recoveryAgentUid) {
int[] types = mRecoverableKeyStoreDb.getRecoverySecretTypes(mUserId, recoveryAgentUid);
- if (!ArrayUtils.contains(types, RecoveryMetadata.TYPE_LOCKSCREEN)) {
+ if (!ArrayUtils.contains(types, KeychainProtectionParameter.TYPE_LOCKSCREEN)) {
// Only lockscreen type is supported.
// We will need to pass extra argument to KeySyncTask to support custom pass phrase.
return false;
@@ -331,14 +330,14 @@
* @return The format - either pattern, pin, or password.
*/
@VisibleForTesting
- @RecoveryMetadata.LockScreenUiFormat static int getUiFormat(
+ @KeychainProtectionParameter.LockScreenUiFormat static int getUiFormat(
int credentialType, String credential) {
if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
- return RecoveryMetadata.TYPE_PATTERN;
+ return KeychainProtectionParameter.TYPE_PATTERN;
} else if (isPin(credential)) {
- return RecoveryMetadata.TYPE_PIN;
+ return KeychainProtectionParameter.TYPE_PIN;
} else {
- return RecoveryMetadata.TYPE_PASSWORD;
+ return KeychainProtectionParameter.TYPE_PASSWORD;
}
}
@@ -401,12 +400,12 @@
return keyGenerator.generateKey();
}
- private static List<EntryRecoveryData> createApplicationKeyEntries(
+ private static List<WrappedApplicationKey> createApplicationKeyEntries(
Map<String, byte[]> encryptedApplicationKeys) {
- ArrayList<EntryRecoveryData> keyEntries = new ArrayList<>();
+ ArrayList<WrappedApplicationKey> keyEntries = new ArrayList<>();
for (String alias : encryptedApplicationKeys.keySet()) {
keyEntries.add(
- new EntryRecoveryData(
+ new WrappedApplicationKey(
alias,
encryptedApplicationKeys.get(alias)));
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 7658178..f14af4b 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -34,9 +34,9 @@
import android.os.ServiceSpecificException;
import android.os.UserHandle;
-import android.security.keystore.EntryRecoveryData;
-import android.security.keystore.RecoveryData;
-import android.security.keystore.RecoveryMetadata;
+import android.security.keystore.KeychainProtectionParameter;
+import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.WrappedApplicationKey;
import android.security.keystore.RecoveryManager;
import android.util.Log;
@@ -45,7 +45,6 @@
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
-import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyStoreException;
import java.security.KeyFactory;
@@ -171,11 +170,12 @@
* @return recovery data
* @hide
*/
- public @NonNull RecoveryData getRecoveryData(@NonNull byte[] account)
+ public @NonNull
+ KeychainSnapshot getRecoveryData(@NonNull byte[] account)
throws RemoteException {
checkRecoverKeyStorePermission();
int uid = Binder.getCallingUid();
- RecoveryData snapshot = mSnapshotStorage.get(uid);
+ KeychainSnapshot snapshot = mSnapshotStorage.get(uid);
if (snapshot == null) {
throw new ServiceSpecificException(ERROR_NO_SNAPSHOT_PENDING);
}
@@ -257,7 +257,7 @@
* @hide
*/
public void setRecoverySecretTypes(
- @NonNull @RecoveryMetadata.UserSecretType int[] secretTypes)
+ @NonNull @KeychainProtectionParameter.UserSecretType int[] secretTypes)
throws RemoteException {
checkRecoverKeyStorePermission();
int userId = UserHandle.getCallingUserId();
@@ -292,9 +292,9 @@
}
public void recoverySecretAvailable(
- @NonNull RecoveryMetadata recoverySecret) throws RemoteException {
+ @NonNull KeychainProtectionParameter recoverySecret) throws RemoteException {
int uid = Binder.getCallingUid();
- if (recoverySecret.getLockScreenUiFormat() == RecoveryMetadata.TYPE_LOCKSCREEN) {
+ if (recoverySecret.getLockScreenUiFormat() == KeychainProtectionParameter.TYPE_LOCKSCREEN) {
throw new SecurityException(
"Caller " + uid + " is not allowed to set lock screen secret");
}
@@ -320,13 +320,13 @@
@NonNull byte[] verifierPublicKey,
@NonNull byte[] vaultParams,
@NonNull byte[] vaultChallenge,
- @NonNull List<RecoveryMetadata> secrets)
+ @NonNull List<KeychainProtectionParameter> secrets)
throws RemoteException {
checkRecoverKeyStorePermission();
int uid = Binder.getCallingUid();
if (secrets.size() != 1) {
- throw new UnsupportedOperationException("Only a single RecoveryMetadata is supported");
+ throw new UnsupportedOperationException("Only a single KeychainProtectionParameter is supported");
}
PublicKey publicKey;
@@ -384,7 +384,7 @@
public Map<String, byte[]> recoverKeys(
@NonNull String sessionId,
@NonNull byte[] encryptedRecoveryKey,
- @NonNull List<EntryRecoveryData> applicationKeys)
+ @NonNull List<WrappedApplicationKey> applicationKeys)
throws RemoteException {
checkRecoverKeyStorePermission();
int uid = Binder.getCallingUid();
@@ -474,9 +474,9 @@
*/
private Map<String, byte[]> recoverApplicationKeys(
@NonNull byte[] recoveryKey,
- @NonNull List<EntryRecoveryData> applicationKeys) throws RemoteException {
+ @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException {
HashMap<String, byte[]> keyMaterialByAlias = new HashMap<>();
- for (EntryRecoveryData applicationKey : applicationKeys) {
+ for (WrappedApplicationKey applicationKey : applicationKeys) {
String alias = applicationKey.getAlias();
byte[] encryptedKeyMaterial = applicationKey.getEncryptedKeyMaterial();
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
index eb2da80..8bba212 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -404,7 +404,7 @@
/**
* Updates the list of user secret types used for end-to-end encryption.
* If no secret types are set, recovery snapshot will not be created.
- * See {@code RecoveryMetadata}
+ * See {@code KeychainProtectionParameter}
*
* @param userId The userId of the profile the application is running under.
* @param uid The uid of the application.
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
index 158b1e3..62bb41e 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
@@ -17,7 +17,7 @@
package com.android.server.locksettings.recoverablekeystore.storage;
import android.annotation.Nullable;
-import android.security.keystore.RecoveryData;
+import android.security.keystore.KeychainSnapshot;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -34,12 +34,12 @@
*/
public class RecoverySnapshotStorage {
@GuardedBy("this")
- private final SparseArray<RecoveryData> mSnapshotByUid = new SparseArray<>();
+ private final SparseArray<KeychainSnapshot> mSnapshotByUid = new SparseArray<>();
/**
* Sets the latest {@code snapshot} for the recovery agent {@code uid}.
*/
- public synchronized void put(int uid, RecoveryData snapshot) {
+ public synchronized void put(int uid, KeychainSnapshot snapshot) {
mSnapshotByUid.put(uid, snapshot);
}
@@ -47,7 +47,7 @@
* Returns the latest snapshot for the recovery agent {@code uid}, or null if none exists.
*/
@Nullable
- public synchronized RecoveryData get(int uid) {
+ public synchronized KeychainSnapshot get(int uid) {
return mSnapshotByUid.get(uid);
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 37e6ae9..42093e8 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -71,6 +71,7 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@@ -98,6 +99,9 @@
static final String ATT_APPROVED_LIST = "approved";
static final String ATT_USER_ID = "user";
static final String ATT_IS_PRIMARY = "primary";
+ static final String ATT_VERSION = "version";
+
+ static final int DB_VERSION = 1;
static final int APPROVAL_BY_PACKAGE = 0;
static final int APPROVAL_BY_COMPONENT = 1;
@@ -295,6 +299,8 @@
public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
out.startTag(null, getConfig().xmlTag);
+ out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION));
+
if (forBackup) {
trimApprovedListsAccordingToInstalledServices();
}
@@ -336,6 +342,14 @@
public void readXml(XmlPullParser parser)
throws XmlPullParserException, IOException {
+ // upgrade xml
+ int xmlVersion = XmlUtils.readIntAttribute(parser, ATT_VERSION, 0);
+ final List<UserInfo> activeUsers = mUm.getUsers(true);
+ for (UserInfo userInfo : activeUsers) {
+ upgradeXml(xmlVersion, userInfo.getUserHandle().getIdentifier());
+ }
+
+ // read grants
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
String tag = parser.getName();
@@ -346,6 +360,7 @@
if (type == XmlPullParser.START_TAG) {
if (TAG_MANAGED_SERVICES.equals(tag)) {
Slog.i(TAG, "Read " + mConfig.caption + " permissions from xml");
+
final String approved = XmlUtils.readStringAttribute(parser, ATT_APPROVED_LIST);
final int userId = XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0);
final boolean isPrimary =
@@ -360,6 +375,8 @@
rebindServices(false);
}
+ protected void upgradeXml(final int xmlVersion, final int userId) {}
+
private void loadAllowedComponentsFromSettings() {
UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
@@ -379,7 +396,7 @@
Slog.d(TAG, "Done loading approved values from settings");
}
- private void addApprovedList(String approved, int userId, boolean isPrimary) {
+ protected void addApprovedList(String approved, int userId, boolean isPrimary) {
if (TextUtils.isEmpty(approved)) {
approved = "";
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 16b9257..2f6618f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -434,6 +434,7 @@
}
}
}
+
String defaultDndAccess = getContext().getResources().getString(
com.android.internal.R.string.config_defaultDndAccessPackages);
if (defaultListenerAccess != null) {
@@ -446,6 +447,29 @@
}
}
}
+
+ readDefaultAssistant(userId);
+ }
+
+ protected void readDefaultAssistant(int userId) {
+ String defaultAssistantAccess = getContext().getResources().getString(
+ com.android.internal.R.string.config_defaultAssistantAccessPackage);
+ if (defaultAssistantAccess != null) {
+ // Gather all notification assistant components for candidate pkg. There should
+ // only be one
+ Set<ComponentName> approvedAssistants =
+ mAssistants.queryPackageForServices(defaultAssistantAccess,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+ for (ComponentName cn : approvedAssistants) {
+ try {
+ getBinderService().setNotificationAssistantAccessGrantedForUser(cn,
+ userId, true);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
}
void readPolicyXml(InputStream stream, boolean forRestore)
@@ -1155,6 +1179,7 @@
}
}
+ @VisibleForTesting
void clearNotifications() {
mEnqueuedNotifications.clear();
mNotificationList.clear();
@@ -1374,7 +1399,8 @@
AppGlobals.getPackageManager(), getContext().getPackageManager(),
getLocalService(LightsManager.class),
new NotificationListeners(AppGlobals.getPackageManager()),
- new NotificationAssistants(AppGlobals.getPackageManager()),
+ new NotificationAssistants(getContext(), mNotificationLock, mUserProfiles,
+ AppGlobals.getPackageManager()),
new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()),
null, snoozeHelper, new NotificationUsageStats(getContext()),
new AtomicFile(new File(systemDir, "notification_policy.xml")),
@@ -5553,8 +5579,9 @@
public class NotificationAssistants extends ManagedServices {
static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";
- public NotificationAssistants(IPackageManager pm) {
- super(getContext(), mNotificationLock, mUserProfiles, pm);
+ public NotificationAssistants(Context context, Object lock, UserProfiles up,
+ IPackageManager pm) {
+ super(context, lock, up, pm);
}
@Override
@@ -5659,6 +5686,14 @@
public boolean isEnabled() {
return !getServices().isEmpty();
}
+
+ protected void upgradeXml(final int xmlVersion, final int userId) {
+ if (xmlVersion == 0) {
+ // one time approval of the OOB assistant
+ Slog.d(TAG, "Approving default notification assistant for user " + userId);
+ readDefaultAssistant(userId);
+ }
+ }
}
public class NotificationListeners extends ManagedServices {
@@ -6072,7 +6107,7 @@
public static final String USAGE = "help\n"
+ "allow_listener COMPONENT [user_id]\n"
+ "disallow_listener COMPONENT [user_id]\n"
- + "set_assistant COMPONENT\n"
+ + "allow_assistant COMPONENT\n"
+ "remove_assistant COMPONENT\n"
+ "allow_dnd PACKAGE\n"
+ "disallow_dnd PACKAGE";
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index d4b437a..f129fe1 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -75,6 +75,7 @@
import android.view.AppTransitionAnimationSpec;
import android.view.DisplayListCanvas;
import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.RemoteAnimationAdapter;
import android.view.RenderNode;
import android.view.ThreadedRenderer;
import android.view.WindowManager;
@@ -213,6 +214,8 @@
* }.
*/
private static final int NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS = 9;
+ private static final int NEXT_TRANSIT_TYPE_REMOTE = 10;
+
private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
// These are the possible states for the enter/exit activities during a thumbnail transition
@@ -275,6 +278,8 @@
private final boolean mGridLayoutRecentsEnabled;
private final boolean mLowRamRecentsEnabled;
+ private RemoteAnimationController mRemoteAnimationController;
+
AppTransition(Context context, WindowManagerService service) {
mContext = context;
mService = service;
@@ -454,6 +459,9 @@
app.startDelayingAnimationStart();
}
}
+ if (mRemoteAnimationController != null) {
+ mRemoteAnimationController.goodToGo();
+ }
return redoLayout;
}
@@ -468,6 +476,7 @@
mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
mNextAppTransitionPackage = null;
mNextAppTransitionAnimationsSpecs.clear();
+ mRemoteAnimationController = null;
mNextAppTransitionAnimationsSpecsFuture = null;
mDefaultNextAppTransitionAnimationSpec = null;
mAnimationFinishedCallback = null;
@@ -1551,6 +1560,10 @@
&& mNextAppTransition != TRANSIT_KEYGUARD_GOING_AWAY;
}
+ RemoteAnimationController getRemoteAnimationController() {
+ return mRemoteAnimationController;
+ }
+
/**
*
* @param frame These are the bounds of the window when it finishes the animation. This is where
@@ -1788,7 +1801,7 @@
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
IRemoteCallback startedCallback) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
mNextAppTransitionPackage = packageName;
@@ -1796,14 +1809,12 @@
mNextAppTransitionExit = exitAnim;
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
- } else {
- postAnimationCallback();
}
}
void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
int startHeight) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null);
@@ -1813,7 +1824,7 @@
void overridePendingAppTransitionClipReveal(int startX, int startY,
int startWidth, int startHeight) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CLIP_REVEAL;
putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null);
@@ -1823,7 +1834,7 @@
void overridePendingAppTransitionThumb(GraphicBuffer srcThumb, int startX, int startY,
IRemoteCallback startedCallback, boolean scaleUp) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN;
@@ -1831,14 +1842,12 @@
putDefaultNextAppTransitionCoordinates(startX, startY, 0, 0, srcThumb);
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
- } else {
- postAnimationCallback();
}
}
void overridePendingAppTransitionAspectScaledThumb(GraphicBuffer srcThumb, int startX, int startY,
int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
@@ -1847,15 +1856,13 @@
srcThumb);
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
- } else {
- postAnimationCallback();
}
}
- public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
+ void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback,
boolean scaleUp) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
@@ -1878,15 +1885,13 @@
postAnimationCallback();
mNextAppTransitionCallback = onAnimationStartedCallback;
mAnimationFinishedCallback = onAnimationFinishedCallback;
- } else {
- postAnimationCallback();
}
}
void overridePendingAppTransitionMultiThumbFuture(
IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
boolean scaleUp) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
@@ -1896,14 +1901,21 @@
}
}
- void overrideInPlaceAppTransition(String packageName, int anim) {
+ void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
if (isTransitionSet()) {
clear();
+ mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
+ mRemoteAnimationController = new RemoteAnimationController(mService,
+ remoteAnimationAdapter, mService.mH);
+ }
+ }
+
+ void overrideInPlaceAppTransition(String packageName, int anim) {
+ if (canOverridePendingAppTransition()) {
+ clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE;
mNextAppTransitionPackage = packageName;
mNextAppTransitionInPlace = anim;
- } else {
- postAnimationCallback();
}
}
@@ -1911,13 +1923,18 @@
* @see {@link #NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS}
*/
void overridePendingAppTransitionStartCrossProfileApps() {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS;
postAnimationCallback();
}
}
+ private boolean canOverridePendingAppTransition() {
+ // Remote animations always take precedence
+ return isTransitionSet() && mNextAppTransitionType != NEXT_TRANSIT_TYPE_REMOTE;
+ }
+
/**
* If a future is set for the app transition specs, fetch it in another thread.
*/
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a254ba2..ed39159 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -378,7 +378,6 @@
// Reset the state of mHiddenSetFromTransferredStartingWindow since visibility is actually
// been set by the app now.
mHiddenSetFromTransferredStartingWindow = false;
- setClientHidden(!visible);
// Allow for state changes and animation to be applied if:
// * token is transitioning visibility state
@@ -463,6 +462,12 @@
mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token);
}
+ // Update the client visibility if we are not running an animation. Otherwise, we'll
+ // update client visibility state in onAnimationFinished.
+ if (!visible && !delayed) {
+ setClientHidden(true);
+ }
+
// If we are hidden but there is no delay needed we immediately
// apply the Surface transaction so that the ActivityManager
// can have some guarantee on the Surface state following
@@ -1611,27 +1616,37 @@
// different animation is running.
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#applyAnimationLocked");
if (okToAnimate()) {
- final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
- if (a != null) {
- final TaskStack stack = getStack();
- mTmpPoint.set(0, 0);
- mTmpRect.setEmpty();
- if (stack != null) {
- stack.getRelativePosition(mTmpPoint);
- stack.getBounds(mTmpRect);
- mTmpRect.offsetTo(0, 0);
+ final AnimationAdapter adapter;
+ final TaskStack stack = getStack();
+ mTmpPoint.set(0, 0);
+ mTmpRect.setEmpty();
+ if (stack != null) {
+ stack.getRelativePosition(mTmpPoint);
+ stack.getBounds(mTmpRect);
+ mTmpRect.offsetTo(0, 0);
+ }
+ if (mService.mAppTransition.getRemoteAnimationController() != null) {
+ adapter = mService.mAppTransition.getRemoteAnimationController()
+ .createAnimationAdapter(this, mTmpPoint, mTmpRect);
+ } else {
+ final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
+ if (a != null) {
+ adapter = new LocalAnimationAdapter(
+ new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
+ mService.mAppTransition.canSkipFirstFrame(),
+ mService.mAppTransition.getAppStackClipMode()),
+ mService.mSurfaceAnimationRunner);
+ if (a.getZAdjustment() == Animation.ZORDER_TOP) {
+ mNeedsZBoost = true;
+ }
+ mTransit = transit;
+ mTransitFlags = mService.mAppTransition.getTransitFlags();
+ } else {
+ adapter = null;
}
- final AnimationAdapter adapter = new LocalAnimationAdapter(
- new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
- mService.mAppTransition.canSkipFirstFrame(),
- mService.mAppTransition.getAppStackClipMode()),
- mService.mSurfaceAnimationRunner);
- if (a.getZAdjustment() == Animation.ZORDER_TOP) {
- mNeedsZBoost = true;
- }
+ }
+ if (adapter != null) {
startAnimation(getPendingTransaction(), adapter, !isVisible());
- mTransit = transit;
- mTransitFlags = mService.mAppTransition.getTransitFlags();
}
} else {
cancelAnimation();
@@ -1754,6 +1769,7 @@
"AppWindowToken");
clearThumbnail();
+ setClientHidden(isHidden());
if (mService.mInputMethodTarget != null && mService.mInputMethodTarget.mAppToken == this) {
getDisplayContent().computeImeTarget(true /* updateImeTarget */);
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
new file mode 100644
index 0000000..688b4ff
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationFinishedCallback.Stub;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
+import java.util.ArrayList;
+
+/**
+ * Helper class to run app animations in a remote process.
+ */
+class RemoteAnimationController {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM;
+ private static final long TIMEOUT_MS = 2000;
+
+ private final WindowManagerService mService;
+ private final RemoteAnimationAdapter mRemoteAnimationAdapter;
+ private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>();
+ private final Rect mTmpRect = new Rect();
+ private final Handler mHandler;
+
+ private final IRemoteAnimationFinishedCallback mFinishedCallback = new Stub() {
+ @Override
+ public void onAnimationFinished() throws RemoteException {
+ RemoteAnimationController.this.onAnimationFinished();
+ }
+ };
+
+ private final Runnable mTimeoutRunnable = () -> {
+ onAnimationFinished();
+ invokeAnimationCancelled();
+ };
+
+ RemoteAnimationController(WindowManagerService service,
+ RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
+ mService = service;
+ mRemoteAnimationAdapter = remoteAnimationAdapter;
+ mHandler = handler;
+ }
+
+ /**
+ * Creates an animation for each individual {@link AppWindowToken}.
+ *
+ * @param appWindowToken The app to animate.
+ * @param position The position app bounds, in screen coordinates.
+ * @param stackBounds The stack bounds of the app.
+ * @return The adapter to be run on the app.
+ */
+ AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position,
+ Rect stackBounds) {
+ final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper(
+ appWindowToken, position, stackBounds);
+ mPendingAnimations.add(adapter);
+ return adapter;
+ }
+
+ /**
+ * Called when the transition is ready to be started, and all leashes have been set up.
+ */
+ void goodToGo() {
+ mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS);
+ try {
+ mRemoteAnimationAdapter.getRunner().onAnimationStart(createAnimations(),
+ mFinishedCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to start remote animation", e);
+ onAnimationFinished();
+ }
+ }
+
+ private RemoteAnimationTarget[] createAnimations() {
+ final RemoteAnimationTarget[] result = new RemoteAnimationTarget[mPendingAnimations.size()];
+ for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+ result[i] = mPendingAnimations.get(i).createRemoteAppAnimation();
+ }
+ return result;
+ }
+
+ private void onAnimationFinished() {
+ mHandler.removeCallbacks(mTimeoutRunnable);
+ synchronized (mService.mWindowMap) {
+ mService.openSurfaceTransaction();
+ try {
+ for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+ final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
+ adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
+ }
+ } finally {
+ mService.closeSurfaceTransaction("RemoteAnimationController#finished");
+ }
+ }
+ }
+
+ private void invokeAnimationCancelled() {
+ try {
+ mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to notify cancel", e);
+ }
+ }
+
+ private class RemoteAnimationAdapterWrapper implements AnimationAdapter {
+
+ private final AppWindowToken mAppWindowToken;
+ private SurfaceControl mCapturedLeash;
+ private OnAnimationFinishedCallback mCapturedFinishCallback;
+ private final Point mPosition = new Point();
+ private final Rect mStackBounds = new Rect();
+
+ RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position,
+ Rect stackBounds) {
+ mAppWindowToken = appWindowToken;
+ mPosition.set(position.x, position.y);
+ mStackBounds.set(stackBounds);
+ }
+
+ RemoteAnimationTarget createRemoteAppAnimation() {
+ return new RemoteAnimationTarget(mAppWindowToken.getTask().mTaskId, getMode(),
+ mCapturedLeash, !mAppWindowToken.fillsParent(),
+ mAppWindowToken.findMainWindow().mWinAnimator.mLastClipRect,
+ mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds);
+ }
+
+ private int getMode() {
+ if (mService.mOpeningApps.contains(mAppWindowToken)) {
+ return RemoteAnimationTarget.MODE_OPENING;
+ } else {
+ return RemoteAnimationTarget.MODE_CLOSING;
+ }
+ }
+
+ @Override
+ public boolean getDetachWallpaper() {
+ return false;
+ }
+
+ @Override
+ public int getBackgroundColor() {
+ return 0;
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, Transaction t,
+ OnAnimationFinishedCallback finishCallback) {
+
+ // Restore z-layering, position and stack crop until client has a chance to modify it.
+ t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex());
+ t.setPosition(animationLeash, mPosition.x, mPosition.y);
+ mTmpRect.set(mStackBounds);
+ mTmpRect.offsetTo(0, 0);
+ t.setWindowCrop(animationLeash, mTmpRect);
+ mCapturedLeash = animationLeash;
+ mCapturedFinishCallback = finishCallback;
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {
+ mPendingAnimations.remove(this);
+ if (mPendingAnimations.isEmpty()) {
+ mHandler.removeCallbacks(mTimeoutRunnable);
+ invokeAnimationCancelled();
+ }
+ }
+
+ @Override
+ public long getDurationHint() {
+ return mRemoteAnimationAdapter.getDuration();
+ }
+
+ @Override
+ public long getStatusBarTransitionsStartTime() {
+ return SystemClock.uptimeMillis()
+ + mRemoteAnimationAdapter.getStatusBarTransitionDelay();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d2ab9df..a91eb4f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.MANAGE_APP_TOKENS;
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
@@ -210,6 +211,7 @@
import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.PointerIcon;
+import android.view.RemoteAnimationAdapter;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Builder;
@@ -2624,6 +2626,18 @@
}
@Override
+ public void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
+ if (!checkCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ "overridePendingAppTransitionRemote()")) {
+ throw new SecurityException(
+ "Requires CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS permission");
+ }
+ synchronized (mWindowMap) {
+ mAppTransition.overridePendingAppTransitionRemote(remoteAnimationAdapter);
+ }
+ }
+
+ @Override
public void endProlongedAnimations() {
synchronized (mWindowMap) {
for (final WindowState win : mWindowMap.values()) {
@@ -6600,6 +6614,7 @@
public void onOverlayChanged() {
synchronized (mWindowMap) {
mPolicy.onOverlayChangedLw();
+ mDisplayManagerInternal.onOverlayChanged();
requestTraversal();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d068c49..db30db0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -193,6 +193,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.input.InputWindowHandle;
import com.android.server.policy.WindowManagerPolicy;
@@ -3952,6 +3953,22 @@
return null;
}
+ /**
+ * @return True if we our one of our ancestors has {@link #mAnimatingExit} set to true, false
+ * otherwise.
+ */
+ @VisibleForTesting
+ boolean isSelfOrAncestorWindowAnimatingExit() {
+ WindowState window = this;
+ do {
+ if (window.mAnimatingExit) {
+ return true;
+ }
+ window = window.getParentWindow();
+ } while (window != null);
+ return false;
+ }
+
void onExitAnimationDone() {
if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
+ ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
@@ -3987,7 +4004,7 @@
mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
- if (!mAnimatingExit) {
+ if (!isSelfOrAncestorWindowAnimatingExit()) {
return;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index cf844f3..4e29935 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4517,6 +4517,28 @@
}
}
+ private boolean canPOorDOCallResetPassword(ActiveAdmin admin, @UserIdInt int userId) {
+ // Only if the admins targets a pre-O SDK
+ return getTargetSdk(admin.info.getPackageName(), userId) < Build.VERSION_CODES.O;
+ }
+
+ /* PO or DO could do an untrusted reset in certain conditions. */
+ private boolean canUserHaveUntrustedCredentialReset(@UserIdInt int userId) {
+ synchronized (this) {
+ // An active DO or PO might be able to fo an untrusted credential reset
+ for (final ActiveAdmin admin : getUserData(userId).mAdminList) {
+ if (!isActiveAdminWithPolicyForUserLocked(admin,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, userId)) {
+ continue;
+ }
+ if (canPOorDOCallResetPassword(admin, userId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
@Override
public boolean resetPassword(String passwordOrNull, int flags) throws RemoteException {
final int callingUid = mInjector.binderGetCallingUid();
@@ -4535,12 +4557,12 @@
null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, callingUid);
final boolean preN;
if (admin != null) {
- final int targetSdk = getTargetSdk(admin.info.getPackageName(), userHandle);
- if (targetSdk >= Build.VERSION_CODES.O) {
+ if (!canPOorDOCallResetPassword(admin, userHandle)) {
throw new SecurityException("resetPassword() is deprecated for DPC targeting O"
+ " or later");
}
- preN = targetSdk <= android.os.Build.VERSION_CODES.M;
+ preN = getTargetSdk(admin.info.getPackageName(),
+ userHandle) <= android.os.Build.VERSION_CODES.M;
} else {
// Otherwise, make sure the caller has any active admin with the right policy.
admin = getActiveAdminForCallerLocked(null,
@@ -10111,6 +10133,11 @@
updateMaximumTimeToLockLocked(userId);
}
}
+
+ @Override
+ public boolean canUserHaveUntrustedCredentialReset(@UserIdInt int userId) {
+ return DevicePolicyManagerService.this.canUserHaveUntrustedCredentialReset(userId);
+ }
}
private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 71ba685..cd4e8f9 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -57,7 +57,9 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.print.DualDumpOutputStream;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
@@ -670,37 +672,29 @@
final long identity = Binder.clearCallingIdentity();
try {
if (dumpAsProto) {
- dump(new ProtoOutputStream(fd), userStatesToDump);
+ dump(new DualDumpOutputStream(new ProtoOutputStream(fd), null),
+ userStatesToDump);
} else {
- dump(fd, pw, userStatesToDump);
+ pw.println("PRINT MANAGER STATE (dumpsys print)");
+
+ dump(new DualDumpOutputStream(null, new IndentingPrintWriter(pw, " ")),
+ userStatesToDump);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- private void dump(@NonNull ProtoOutputStream proto,
+ private void dump(@NonNull DualDumpOutputStream dumpStream,
@NonNull ArrayList<UserState> userStatesToDump) {
final int userStateCount = userStatesToDump.size();
for (int i = 0; i < userStateCount; i++) {
- long token = proto.start(PrintServiceDumpProto.USER_STATES);
- userStatesToDump.get(i).dump(proto);
- proto.end(token);
+ long token = dumpStream.start("user_states", PrintServiceDumpProto.USER_STATES);
+ userStatesToDump.get(i).dump(dumpStream);
+ dumpStream.end(token);
}
- proto.flush();
- }
-
- private void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
- @NonNull ArrayList<UserState> userStatesToDump) {
- pw = Preconditions.checkNotNull(pw);
-
- pw.println("PRINT MANAGER STATE (dumpsys print)");
- final int userStateCount = userStatesToDump.size();
- for (int i = 0; i < userStateCount; i++) {
- userStatesToDump.get(i).dump(fd, pw, "");
- pw.println();
- }
+ dumpStream.flush();
}
private void registerContentObservers() {
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index 13462cd..80b97cf 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -47,11 +47,10 @@
import android.printservice.IPrintServiceClient;
import android.service.print.ActivePrintServiceProto;
import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.print.DualDumpOutputStream;
-import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -532,49 +531,30 @@
}
}
- public void dump(@NonNull ProtoOutputStream proto) {
- writeComponentName(proto, ActivePrintServiceProto.COMPONENT_NAME, mComponentName);
+ public void dump(@NonNull DualDumpOutputStream proto) {
+ writeComponentName(proto, "component_name", ActivePrintServiceProto.COMPONENT_NAME,
+ mComponentName);
- proto.write(ActivePrintServiceProto.IS_DESTROYED, mDestroyed);
- proto.write(ActivePrintServiceProto.IS_BOUND, isBound());
- proto.write(ActivePrintServiceProto.HAS_DISCOVERY_SESSION, mHasPrinterDiscoverySession);
- proto.write(ActivePrintServiceProto.HAS_ACTIVE_PRINT_JOBS, mHasActivePrintJobs);
- proto.write(ActivePrintServiceProto.IS_DISCOVERING_PRINTERS,
+ proto.write("is_destroyed", ActivePrintServiceProto.IS_DESTROYED, mDestroyed);
+ proto.write("is_bound", ActivePrintServiceProto.IS_BOUND, isBound());
+ proto.write("has_discovery_session", ActivePrintServiceProto.HAS_DISCOVERY_SESSION,
+ mHasPrinterDiscoverySession);
+ proto.write("has_active_print_jobs", ActivePrintServiceProto.HAS_ACTIVE_PRINT_JOBS,
+ mHasActivePrintJobs);
+ proto.write("is_discovering_printers", ActivePrintServiceProto.IS_DISCOVERING_PRINTERS,
mDiscoveryPriorityList != null);
synchronized (mLock) {
if (mTrackedPrinterList != null) {
int numTrackedPrinters = mTrackedPrinterList.size();
for (int i = 0; i < numTrackedPrinters; i++) {
- writePrinterId(proto, ActivePrintServiceProto.TRACKED_PRINTERS,
- mTrackedPrinterList.get(i));
+ writePrinterId(proto, "tracked_printers",
+ ActivePrintServiceProto.TRACKED_PRINTERS, mTrackedPrinterList.get(i));
}
}
}
}
- public void dump(PrintWriter pw, String prefix) {
- String tab = " ";
- pw.append(prefix).append("service:").println();
- pw.append(prefix).append(tab).append("componentName=")
- .append(mComponentName.flattenToString()).println();
- pw.append(prefix).append(tab).append("destroyed=")
- .append(String.valueOf(mDestroyed)).println();
- pw.append(prefix).append(tab).append("bound=")
- .append(String.valueOf(isBound())).println();
- pw.append(prefix).append(tab).append("hasDicoverySession=")
- .append(String.valueOf(mHasPrinterDiscoverySession)).println();
- pw.append(prefix).append(tab).append("hasActivePrintJobs=")
- .append(String.valueOf(mHasActivePrintJobs)).println();
- pw.append(prefix).append(tab).append("isDiscoveringPrinters=")
- .append(String.valueOf(mDiscoveryPriorityList != null)).println();
-
- synchronized (mLock) {
- pw.append(prefix).append(tab).append("trackedPrinters=").append(
- (mTrackedPrinterList != null) ? mTrackedPrinterList.toString() : "null");
- }
- }
-
private boolean isBound() {
return mPrintService != null;
}
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index f654fcb..a69baa1 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -43,16 +43,14 @@
import android.service.print.PrintSpoolerStateProto;
import android.util.Slog;
import android.util.TimedRemoteCaller;
-import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.TransferPipe;
+import com.android.internal.print.DualDumpOutputStream;
import libcore.io.IoUtils;
-import java.io.FileDescriptor;
import java.io.IOException;
-import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.concurrent.TimeoutException;
@@ -558,37 +556,25 @@
}
}
- public void dump(@NonNull ProtoOutputStream proto) {
+ public void dump(@NonNull DualDumpOutputStream dumpStream) {
synchronized (mLock) {
- proto.write(PrintSpoolerStateProto.IS_DESTROYED, mDestroyed);
- proto.write(PrintSpoolerStateProto.IS_BOUND, mRemoteInstance != null);
+ dumpStream.write("is_destroyed", PrintSpoolerStateProto.IS_DESTROYED, mDestroyed);
+ dumpStream.write("is_bound", PrintSpoolerStateProto.IS_BOUND, mRemoteInstance != null);
}
try {
- proto.write(PrintSpoolerStateProto.INTERNAL_STATE,
- TransferPipe.dumpAsync(getRemoteInstanceLazy().asBinder(), "--proto"));
+ if (dumpStream.isProto()) {
+ dumpStream.write(null, PrintSpoolerStateProto.INTERNAL_STATE,
+ TransferPipe.dumpAsync(getRemoteInstanceLazy().asBinder(), "--proto"));
+ } else {
+ dumpStream.writeNested("internal_state", TransferPipe.dumpAsync(
+ getRemoteInstanceLazy().asBinder()));
+ }
} catch (IOException | TimeoutException | RemoteException | InterruptedException e) {
Slog.e(LOG_TAG, "Failed to dump remote instance", e);
}
}
- public void dump(FileDescriptor fd, PrintWriter pw, String prefix) {
- synchronized (mLock) {
- pw.append(prefix).append("destroyed=")
- .append(String.valueOf(mDestroyed)).println();
- pw.append(prefix).append("bound=")
- .append((mRemoteInstance != null) ? "true" : "false").println();
-
- pw.flush();
- try {
- TransferPipe.dumpAsync(getRemoteInstanceLazy().asBinder(), fd,
- new String[] { prefix });
- } catch (IOException | TimeoutException | RemoteException | InterruptedException e) {
- pw.println("Failed to dump remote instance: " + e);
- }
- }
- }
-
private void onAllPrintJobsHandled() {
synchronized (mLock) {
throwIfDestroyedLocked();
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 364bbc0..e2808e8 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -76,19 +76,17 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.print.DualDumpOutputStream;
import com.android.server.print.RemotePrintService.PrintServiceCallbacks;
import com.android.server.print.RemotePrintServiceRecommendationService
.RemotePrintServiceRecommendationServiceCallbacks;
import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -817,112 +815,63 @@
mDestroyed = true;
}
- public void dump(@NonNull ProtoOutputStream proto) {
+ public void dump(@NonNull DualDumpOutputStream dumpStream) {
synchronized (mLock) {
- proto.write(PrintUserStateProto.USER_ID, mUserId);
+ dumpStream.write("user_id", PrintUserStateProto.USER_ID, mUserId);
final int installedServiceCount = mInstalledServices.size();
for (int i = 0; i < installedServiceCount; i++) {
- long token = proto.start(PrintUserStateProto.INSTALLED_SERVICES);
+ long token = dumpStream.start("installed_services",
+ PrintUserStateProto.INSTALLED_SERVICES);
PrintServiceInfo installedService = mInstalledServices.get(i);
ResolveInfo resolveInfo = installedService.getResolveInfo();
- writeComponentName(proto, InstalledPrintServiceProto.COMPONENT_NAME,
+ writeComponentName(dumpStream, "component_name",
+ InstalledPrintServiceProto.COMPONENT_NAME,
new ComponentName(resolveInfo.serviceInfo.packageName,
resolveInfo.serviceInfo.name));
- writeStringIfNotNull(proto, InstalledPrintServiceProto.SETTINGS_ACTIVITY,
+ writeStringIfNotNull(dumpStream, "settings_activity",
+ InstalledPrintServiceProto.SETTINGS_ACTIVITY,
installedService.getSettingsActivityName());
- writeStringIfNotNull(proto, InstalledPrintServiceProto.ADD_PRINTERS_ACTIVITY,
+ writeStringIfNotNull(dumpStream, "add_printers_activity",
+ InstalledPrintServiceProto.ADD_PRINTERS_ACTIVITY,
installedService.getAddPrintersActivityName());
- writeStringIfNotNull(proto, InstalledPrintServiceProto.ADVANCED_OPTIONS_ACTIVITY,
+ writeStringIfNotNull(dumpStream, "advanced_options_activity",
+ InstalledPrintServiceProto.ADVANCED_OPTIONS_ACTIVITY,
installedService.getAdvancedOptionsActivityName());
- proto.end(token);
+ dumpStream.end(token);
}
for (ComponentName disabledService : mDisabledServices) {
- writeComponentName(proto, PrintUserStateProto.DISABLED_SERVICES, disabledService);
+ writeComponentName(dumpStream, "disabled_services",
+ PrintUserStateProto.DISABLED_SERVICES, disabledService);
}
final int activeServiceCount = mActiveServices.size();
for (int i = 0; i < activeServiceCount; i++) {
- long token = proto.start(PrintUserStateProto.ACTIVE_SERVICES);
- mActiveServices.valueAt(i).dump(proto);
- proto.end(token);
+ long token = dumpStream.start("actives_services",
+ PrintUserStateProto.ACTIVE_SERVICES);
+ mActiveServices.valueAt(i).dump(dumpStream);
+ dumpStream.end(token);
}
- mPrintJobForAppCache.dumpLocked(proto);
+ mPrintJobForAppCache.dumpLocked(dumpStream);
if (mPrinterDiscoverySession != null) {
- long token = proto.start(PrintUserStateProto.DISCOVERY_SESSIONS);
- mPrinterDiscoverySession.dumpLocked(proto);
- proto.end(token);
+ long token = dumpStream.start("discovery_service",
+ PrintUserStateProto.DISCOVERY_SESSIONS);
+ mPrinterDiscoverySession.dumpLocked(dumpStream);
+ dumpStream.end(token);
}
}
- long token = proto.start(PrintUserStateProto.PRINT_SPOOLER_STATE);
- mSpooler.dump(proto);
- proto.end(token);
- }
-
- public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String prefix) {
- pw.append(prefix).append("user state ").append(String.valueOf(mUserId)).append(":");
- pw.println();
-
- String tab = " ";
-
- synchronized (mLock) {
- pw.append(prefix).append(tab).append("installed services:").println();
- final int installedServiceCount = mInstalledServices.size();
- for (int i = 0; i < installedServiceCount; i++) {
- PrintServiceInfo installedService = mInstalledServices.get(i);
- String installedServicePrefix = prefix + tab + tab;
- pw.append(installedServicePrefix).append("service:").println();
- ResolveInfo resolveInfo = installedService.getResolveInfo();
- ComponentName componentName = new ComponentName(
- resolveInfo.serviceInfo.packageName,
- resolveInfo.serviceInfo.name);
- pw.append(installedServicePrefix).append(tab).append("componentName=")
- .append(componentName.flattenToString()).println();
- pw.append(installedServicePrefix).append(tab).append("settingsActivity=")
- .append(installedService.getSettingsActivityName()).println();
- pw.append(installedServicePrefix).append(tab).append("addPrintersActivity=")
- .append(installedService.getAddPrintersActivityName()).println();
- pw.append(installedServicePrefix).append(tab).append("avancedOptionsActivity=")
- .append(installedService.getAdvancedOptionsActivityName()).println();
- }
-
- pw.append(prefix).append(tab).append("disabled services:").println();
- for (ComponentName disabledService : mDisabledServices) {
- String disabledServicePrefix = prefix + tab + tab;
- pw.append(disabledServicePrefix).append("service:").println();
- pw.append(disabledServicePrefix).append(tab).append("componentName=")
- .append(disabledService.flattenToString());
- pw.println();
- }
-
- pw.append(prefix).append(tab).append("active services:").println();
- final int activeServiceCount = mActiveServices.size();
- for (int i = 0; i < activeServiceCount; i++) {
- RemotePrintService activeService = mActiveServices.valueAt(i);
- activeService.dump(pw, prefix + tab + tab);
- pw.println();
- }
-
- pw.append(prefix).append(tab).append("cached print jobs:").println();
- mPrintJobForAppCache.dumpLocked(pw, prefix + tab + tab);
-
- pw.append(prefix).append(tab).append("discovery mediator:").println();
- if (mPrinterDiscoverySession != null) {
- mPrinterDiscoverySession.dumpLocked(pw, prefix + tab + tab);
- }
- }
-
- pw.append(prefix).append(tab).append("print spooler:").println();
- mSpooler.dump(fd, pw, prefix + tab + tab);
- pw.println();
+ long token = dumpStream.start("print_spooler_state",
+ PrintUserStateProto.PRINT_SPOOLER_STATE);
+ mSpooler.dump(dumpStream);
+ dumpStream.end(token);
}
private void readConfigurationLocked() {
@@ -1650,15 +1599,17 @@
}
}
- public void dumpLocked(@NonNull ProtoOutputStream proto) {
- proto.write(PrinterDiscoverySessionProto.IS_DESTROYED, mDestroyed);
- proto.write(PrinterDiscoverySessionProto.IS_PRINTER_DISCOVERY_IN_PROGRESS,
+ public void dumpLocked(@NonNull DualDumpOutputStream dumpStream) {
+ dumpStream.write("is_destroyed", PrinterDiscoverySessionProto.IS_DESTROYED, mDestroyed);
+ dumpStream.write("is_printer_discovery_in_progress",
+ PrinterDiscoverySessionProto.IS_PRINTER_DISCOVERY_IN_PROGRESS,
!mStartedPrinterDiscoveryTokens.isEmpty());
final int observerCount = mDiscoveryObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
- proto.write(PrinterDiscoverySessionProto.PRINTER_DISCOVERY_OBSERVERS,
+ dumpStream.write("printer_discovery_observers",
+ PrinterDiscoverySessionProto.PRINTER_DISCOVERY_OBSERVERS,
observer.toString());
}
mDiscoveryObservers.finishBroadcast();
@@ -1666,61 +1617,22 @@
final int tokenCount = this.mStartedPrinterDiscoveryTokens.size();
for (int i = 0; i < tokenCount; i++) {
IBinder token = mStartedPrinterDiscoveryTokens.get(i);
- proto.write(PrinterDiscoverySessionProto.DISCOVERY_REQUESTS, token.toString());
+ dumpStream.write("discovery_requests",
+ PrinterDiscoverySessionProto.DISCOVERY_REQUESTS, token.toString());
}
final int trackedPrinters = mStateTrackedPrinters.size();
for (int i = 0; i < trackedPrinters; i++) {
PrinterId printer = mStateTrackedPrinters.get(i);
- writePrinterId(proto, PrinterDiscoverySessionProto.TRACKED_PRINTER_REQUESTS,
- printer);
+ writePrinterId(dumpStream, "tracked_printer_requests",
+ PrinterDiscoverySessionProto.TRACKED_PRINTER_REQUESTS, printer);
}
final int printerCount = mPrinters.size();
for (int i = 0; i < printerCount; i++) {
PrinterInfo printer = mPrinters.valueAt(i);
- writePrinterInfo(mContext, proto, PrinterDiscoverySessionProto.PRINTER, printer);
- }
- }
-
- public void dumpLocked(PrintWriter pw, String prefix) {
- pw.append(prefix).append("destroyed=")
- .append(String.valueOf(mDestroyed)).println();
-
- pw.append(prefix).append("printDiscoveryInProgress=")
- .append(String.valueOf(!mStartedPrinterDiscoveryTokens.isEmpty())).println();
-
- String tab = " ";
-
- pw.append(prefix).append(tab).append("printer discovery observers:").println();
- final int observerCount = mDiscoveryObservers.beginBroadcast();
- for (int i = 0; i < observerCount; i++) {
- IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
- pw.append(prefix).append(prefix).append(observer.toString());
- pw.println();
- }
- mDiscoveryObservers.finishBroadcast();
-
- pw.append(prefix).append(tab).append("start discovery requests:").println();
- final int tokenCount = this.mStartedPrinterDiscoveryTokens.size();
- for (int i = 0; i < tokenCount; i++) {
- IBinder token = mStartedPrinterDiscoveryTokens.get(i);
- pw.append(prefix).append(tab).append(tab).append(token.toString()).println();
- }
-
- pw.append(prefix).append(tab).append("tracked printer requests:").println();
- final int trackedPrinters = mStateTrackedPrinters.size();
- for (int i = 0; i < trackedPrinters; i++) {
- PrinterId printer = mStateTrackedPrinters.get(i);
- pw.append(prefix).append(tab).append(tab).append(printer.toString()).println();
- }
-
- pw.append(prefix).append(tab).append("printers:").println();
- final int pritnerCount = mPrinters.size();
- for (int i = 0; i < pritnerCount; i++) {
- PrinterInfo printer = mPrinters.valueAt(i);
- pw.append(prefix).append(tab).append(tab).append(
- printer.toString()).println();
+ writePrinterInfo(mContext, dumpStream, "printer",
+ PrinterDiscoverySessionProto.PRINTER, printer);
}
}
@@ -1933,36 +1845,22 @@
}
}
- public void dumpLocked(PrintWriter pw, String prefix) {
- String tab = " ";
- final int bucketCount = mPrintJobsForRunningApp.size();
- for (int i = 0; i < bucketCount; i++) {
- final int appId = mPrintJobsForRunningApp.keyAt(i);
- pw.append(prefix).append("appId=" + appId).append(':').println();
- List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i);
- final int printJobCount = bucket.size();
- for (int j = 0; j < printJobCount; j++) {
- PrintJobInfo printJob = bucket.get(j);
- pw.append(prefix).append(tab).append(printJob.toString()).println();
- }
- }
- }
-
- public void dumpLocked(@NonNull ProtoOutputStream proto) {
+ public void dumpLocked(@NonNull DualDumpOutputStream dumpStream) {
final int bucketCount = mPrintJobsForRunningApp.size();
for (int i = 0; i < bucketCount; i++) {
final int appId = mPrintJobsForRunningApp.keyAt(i);
List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i);
final int printJobCount = bucket.size();
for (int j = 0; j < printJobCount; j++) {
- long token = proto.start(PrintUserStateProto.CACHED_PRINT_JOBS);
+ long token = dumpStream.start("cached_print_jobs",
+ PrintUserStateProto.CACHED_PRINT_JOBS);
- proto.write(CachedPrintJobProto.APP_ID, appId);
+ dumpStream.write("app_id", CachedPrintJobProto.APP_ID, appId);
- writePrintJobInfo(mContext, proto, CachedPrintJobProto.PRINT_JOB,
- bucket.get(j));
+ writePrintJobInfo(mContext, dumpStream, "print_job",
+ CachedPrintJobProto.PRINT_JOB, bucket.get(j));
- proto.end(token);
+ dumpStream.end(token);
}
}
}
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java
index dc0a4e3..4ff24e9 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -46,7 +46,6 @@
import com.android.server.backup.testing.ShadowAppBackupUtils;
import com.android.server.backup.testing.ShadowBackupPolicyEnforcer;
import com.android.server.backup.testing.TransportData;
-import com.android.server.backup.testing.TransportTestUtils;
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
import com.android.server.backup.transport.TransportNotRegisteredException;
import com.android.server.testing.FrameworkRobolectricTestRunner;
@@ -68,6 +67,7 @@
import java.io.File;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
@RunWith(FrameworkRobolectricTestRunner.class)
@@ -255,17 +255,22 @@
/* Tests for select transport */
- private TransportData mNewTransport;
- private TransportData mOldTransport;
private ComponentName mNewTransportComponent;
+ private TransportData mNewTransport;
+ private TransportMock mNewTransportMock;
private ComponentName mOldTransportComponent;
+ private TransportData mOldTransport;
+ private TransportMock mOldTransportMock;
private void setUpForSelectTransport() throws Exception {
mNewTransport = backupTransport();
mNewTransportComponent = mNewTransport.getTransportComponent();
mOldTransport = d2dTransport();
mOldTransportComponent = mOldTransport.getTransportComponent();
- setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport());
+ List<TransportMock> transportMocks =
+ setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport());
+ mNewTransportMock = transportMocks.get(0);
+ mOldTransportMock = transportMocks.get(1);
when(mTransportManager.selectTransport(eq(mNewTransport.transportName)))
.thenReturn(mOldTransport.transportName);
}
@@ -282,6 +287,8 @@
assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
assertThat(oldTransport).isEqualTo(mOldTransport.transportName);
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
}
@Test
@@ -311,6 +318,8 @@
mShadowBackupLooper.runToEndOfTasks();
assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
verify(callback).onSuccess(eq(mNewTransport.transportName));
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
}
@Test
@@ -329,11 +338,12 @@
mShadowBackupLooper.runToEndOfTasks();
assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
verify(callback).onSuccess(eq(mNewTransport.transportName));
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
}
@Test
- public void testSelectBackupTransportAsync_whenOtherThanMandatoryTransport()
- throws Exception {
+ public void testSelectBackupTransportAsync_whenOtherThanMandatoryTransport() throws Exception {
setUpForSelectTransport();
ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(mOldTransportComponent);
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
index 9b4dec6..10442b7 100644
--- a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
+++ b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
@@ -35,8 +35,10 @@
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import com.android.internal.backup.IBackupTransport;
+import com.android.server.EventLogTags;
import com.android.server.backup.TransportManager;
import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.ShadowEventLog;
import com.android.server.testing.SystemLoaderClasses;
import org.junit.Before;
import org.junit.Test;
@@ -48,7 +50,7 @@
import org.robolectric.shadows.ShadowLooper;
@RunWith(FrameworkRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, sdk = 26)
+@Config(manifest = Config.NONE, sdk = 26, shadows = {ShadowEventLog.class})
@SystemLoaderClasses({TransportManager.class, TransportClient.class})
@Presubmit
public class TransportClientTest {
@@ -60,6 +62,7 @@
@Mock private IBackupTransport.Stub mIBackupTransport;
private TransportClient mTransportClient;
private ComponentName mTransportComponent;
+ private String mTransportString;
private Intent mBindIntent;
private ShadowLooper mShadowLooper;
@@ -71,6 +74,7 @@
mShadowLooper = shadowOf(mainLooper);
mTransportComponent =
new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".transport.Transport");
+ mTransportString = mTransportComponent.flattenToShortString();
mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent);
mTransportClient =
new TransportClient(
@@ -161,7 +165,7 @@
}
@Test
- public void testConnectAsync_whenFrameworkDoesntBind_releasesConnection() throws Exception {
+ public void testConnectAsync_whenFrameworkDoesNotBind_releasesConnection() throws Exception {
when(mContext.bindServiceAsUser(
eq(mBindIntent),
any(ServiceConnection.class),
@@ -234,6 +238,82 @@
.onTransportConnectionResult(isNull(), eq(mTransportClient));
}
+ @Test
+ public void testConnectAsync_beforeFrameworkCall_logsBoundTransition() {
+ ShadowEventLog.clearEvents();
+
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
+ }
+
+ @Test
+ public void testConnectAsync_afterOnServiceConnected_logsBoundAndConnectedTransitions() {
+ ShadowEventLog.clearEvents();
+
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 1);
+ }
+
+ @Test
+ public void testConnectAsync_afterOnBindingDied_logsBoundAndUnboundTransitions() {
+ ShadowEventLog.clearEvents();
+
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onBindingDied(mTransportComponent);
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
+ }
+
+ @Test
+ public void testUnbind_whenConnected_logsDisconnectedAndUnboundTransitions() {
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
+ ShadowEventLog.clearEvents();
+
+ mTransportClient.unbind("caller1");
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 0);
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
+ }
+
+ @Test
+ public void testOnServiceDisconnected_whenConnected_logsDisconnectedAndUnboundTransitions() {
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
+ ShadowEventLog.clearEvents();
+
+ connection.onServiceDisconnected(mTransportComponent);
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 0);
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
+ }
+
+ @Test
+ public void testOnBindingDied_whenConnected_logsDisconnectedAndUnboundTransitions() {
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
+ ShadowEventLog.clearEvents();
+
+ connection.onBindingDied(mTransportComponent);
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 0);
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
+ }
+
+ private void assertEventLogged(int tag, Object... values) {
+ assertThat(ShadowEventLog.hasEvent(tag, values)).isTrue();
+ }
+
private ServiceConnection verifyBindServiceAsUserAndCaptureServiceConnection(Context context) {
ArgumentCaptor<ServiceConnection> connectionCaptor =
ArgumentCaptor.forClass(ServiceConnection.class);
diff --git a/services/robotests/src/com/android/server/testing/ShadowEventLog.java b/services/robotests/src/com/android/server/testing/ShadowEventLog.java
new file mode 100644
index 0000000..b8059f4
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/ShadowEventLog.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.testing;
+
+import android.util.EventLog;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+@Implements(EventLog.class)
+public class ShadowEventLog {
+ private final static LinkedHashSet<Entry> ENTRIES = new LinkedHashSet<>();
+
+ @Implementation
+ public static int writeEvent(int tag, Object... values) {
+ ENTRIES.add(new Entry(tag, Arrays.asList(values)));
+ // Currently we don't care about the return value, if we do, estimate it correctly
+ return 0;
+ }
+
+ public static boolean hasEvent(int tag, Object... values) {
+ return ENTRIES.contains(new Entry(tag, Arrays.asList(values)));
+ }
+
+ public static void clearEvents() {
+ ENTRIES.clear();
+ }
+
+ public static class Entry {
+ public final int tag;
+ public final List<Object> values;
+
+ public Entry(int tag, List<Object> values) {
+ this.tag = tag;
+ this.values = values;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Entry entry = (Entry) o;
+ return tag == entry.tag && values.equals(entry.values);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = tag;
+ result = 31 * result + values.hashCode();
+ return result;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
index 66d0da1..6a21931 100644
--- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
@@ -42,6 +42,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager.ServiceType;
@@ -50,13 +51,17 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.Global;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.test.mock.MockContentResolver;
import android.util.ArraySet;
import android.util.Pair;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.ForceAppStandbyTracker.Listener;
import org.junit.Before;
@@ -102,6 +107,9 @@
PowerManagerInternal injectPowerManagerInternal() {
return mMockPowerManagerInternal;
}
+
+ @Override
+ boolean isSmallBatteryDevice() { return mIsSmallBatteryDevice; };
}
private static final int UID_1 = Process.FIRST_APPLICATION_UID + 1;
@@ -137,7 +145,11 @@
private Consumer<PowerSaveState> mPowerSaveObserver;
private BroadcastReceiver mReceiver;
+ private MockContentResolver mMockContentResolver;
+ private FakeSettingsProvider mFakeSettingsProvider;
+
private boolean mPowerSaveMode;
+ private boolean mIsSmallBatteryDevice;
private final ArraySet<Pair<Integer, String>> mRestrictedPackages = new ArraySet();
@@ -174,13 +186,17 @@
}
private void callStart(ForceAppStandbyTrackerTestable instance) throws RemoteException {
-
// Set up functions that start() calls.
when(mMockPowerManagerInternal.getLowPowerState(eq(ServiceType.FORCE_ALL_APPS_STANDBY)))
.thenAnswer(inv -> getPowerSaveState());
when(mMockAppOpsManager.getPackagesForOps(
any(int[].class)
- )).thenAnswer(inv -> new ArrayList<AppOpsManager.PackageOps>());
+ )).thenAnswer(inv -> new ArrayList<AppOpsManager.PackageOps>());
+
+ mMockContentResolver = new MockContentResolver();
+ mFakeSettingsProvider = new FakeSettingsProvider();
+ when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
+ mMockContentResolver.addProvider(Settings.AUTHORITY, mFakeSettingsProvider);
// Call start.
instance.start();
@@ -208,7 +224,6 @@
verify(mMockPowerManagerInternal).registerLowPowerModeObserver(
eq(ServiceType.FORCE_ALL_APPS_STANDBY),
powerSaveObserverCaptor.capture());
-
verify(mMockContext).registerReceiver(
receiverCaptor.capture(), any(IntentFilter.class));
@@ -221,6 +236,7 @@
assertNotNull(mAppOpsCallback);
assertNotNull(mPowerSaveObserver);
assertNotNull(mReceiver);
+ assertNotNull(instance.mFlagsObserver);
}
private void setAppOps(int uid, String packageName, boolean restrict) throws RemoteException {
@@ -822,6 +838,33 @@
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
}
+ @Test
+ public void testSmallBatteryAndCharging() throws Exception {
+ // This is a small battery device
+ mIsSmallBatteryDevice = true;
+
+ final ForceAppStandbyTrackerTestable instance = newInstance();
+ callStart(instance);
+ assertFalse(instance.isForceAllAppsStandbyEnabled());
+
+ // Setting/experiment for all app standby for small battery is enabled
+ Global.putInt(mMockContentResolver, Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 1);
+ instance.mFlagsObserver.onChange(true,
+ Global.getUriFor(Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED));
+ assertTrue(instance.isForceAllAppsStandbyEnabled());
+
+ // When battery is charging, force app standby is disabled
+ Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
+ mReceiver.onReceive(mMockContext, intent);
+ assertFalse(instance.isForceAllAppsStandbyEnabled());
+
+ // When battery stops charging, force app standby is enabled
+ intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_DISCHARGING);
+ mReceiver.onReceive(mMockContext, intent);
+ assertTrue(instance.isForceAllAppsStandbyEnabled());
+ }
+
static int[] array(int... appIds) {
Arrays.sort(appIds);
return appIds;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index ccf2aaf..272b5d8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -27,6 +27,7 @@
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManagerInternal;
import android.app.trust.TrustManager;
import android.content.ComponentName;
import android.content.pm.UserInfo;
@@ -41,6 +42,7 @@
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.LocalServices;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@@ -75,6 +77,7 @@
FakeStorageManager mStorageManager;
IActivityManager mActivityManager;
DevicePolicyManager mDevicePolicyManager;
+ DevicePolicyManagerInternal mDevicePolicyManagerInternal;
KeyStore mKeyStore;
MockSyntheticPasswordManager mSpManager;
@@ -88,6 +91,10 @@
mStorageManager = new FakeStorageManager();
mActivityManager = mock(IActivityManager.class);
mDevicePolicyManager = mock(DevicePolicyManager.class);
+ mDevicePolicyManagerInternal = mock(DevicePolicyManagerInternal.class);
+
+ LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+ LocalServices.addService(DevicePolicyManagerInternal.class, mDevicePolicyManagerInternal);
mContext = new MockLockSettingsContext(getContext(), mUserManager, mNotificationManager,
mDevicePolicyManager, mock(StorageManager.class), mock(TrustManager.class));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java
new file mode 100644
index 0000000..4ad9f19
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.locksettings;
+
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.server.testutils.TestUtils.assertExpectException;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.os.RemoteException;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
+
+/**
+ * Run the synthetic password tests with caching enabled.
+ *
+ * By default, those tests run without caching. Untrusted credential reset depends on caching so
+ * this class included those tests.
+ */
+public class CachedSyntheticPasswordTests extends SyntheticPasswordTests {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ enableSpCaching(true);
+ }
+
+ private void enableSpCaching(boolean enable) {
+ when(mDevicePolicyManagerInternal
+ .canUserHaveUntrustedCredentialReset(anyInt())).thenReturn(enable);
+ }
+
+ public void testSyntheticPasswordClearCredentialUntrusted() throws RemoteException {
+ final String PASSWORD = "testSyntheticPasswordClearCredential-password";
+ final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
+
+ initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
+ // clear password
+ mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, null,
+ PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
+ assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
+
+ // set a new password
+ mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+ PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ .getResponseCode());
+ assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
+ }
+
+ public void testSyntheticPasswordChangeCredentialUntrusted() throws RemoteException {
+ final String PASSWORD = "testSyntheticPasswordClearCredential-password";
+ final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
+
+ initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
+ // Untrusted change password
+ mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+ PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ assertNotEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
+ assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
+
+ // Verify the password
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ .getResponseCode());
+ }
+
+ public void testUntrustedCredentialChangeBlockedIfSpNotCached() throws RemoteException {
+ final String PASSWORD = "testUntrustedCredentialChangeBlockedIfSpNotCached-password";
+ final String NEWPASSWORD = "testUntrustedCredentialChangeBlockedIfSpNotCached-newpassword";
+
+ // Disable caching for this test
+ enableSpCaching(false);
+
+ initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
+ // Untrusted change password
+ assertExpectException(IllegalStateException.class, /* messageRegex= */ null,
+ () -> mService.setLockCredential(
+ NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID));
+ assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
+
+ // Verify the new password doesn't work but the old one still does
+ assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential(
+ NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ .getResponseCode());
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ .getResponseCode());
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 2e4c74f..b07d6ac 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -112,7 +112,7 @@
mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
}
- private void initializeCredentialUnderSP(String password, int userId) throws RemoteException {
+ protected void initializeCredentialUnderSP(String password, int userId) throws RemoteException {
enableSyntheticPassword();
int quality = password != null ? PASSWORD_QUALITY_ALPHABETIC
: PASSWORD_QUALITY_UNSPECIFIED;
@@ -129,7 +129,6 @@
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, PASSWORD,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
- mGateKeeperService.clearSecureUserId(PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
.getResponseCode());
@@ -170,44 +169,6 @@
assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
}
- public void testSyntheticPasswordClearCredentialUntrusted() throws RemoteException {
- final String PASSWORD = "testSyntheticPasswordClearCredential-password";
- final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
-
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
- long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
- // clear password
- mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, null,
- PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
- assertEquals(0 ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
-
- // set a new password
- mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
- .getResponseCode());
- assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
- }
-
- public void testSyntheticPasswordChangeCredentialUntrusted() throws RemoteException {
- final String PASSWORD = "testSyntheticPasswordClearCredential-password";
- final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
-
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
- long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
- // Untrusted change password
- mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
- assertNotEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
- assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
-
- // Verify the password
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
- .getResponseCode());
- }
-
public void testManagedProfileUnifiedChallengeMigration() throws RemoteException {
final String UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd";
disableSyntheticPassword();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index 9eb42e9..c1789ba 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -16,11 +16,11 @@
package com.android.server.locksettings.recoverablekeystore;
-import static android.security.keystore.RecoveryMetadata.TYPE_LOCKSCREEN;
+import static android.security.keystore.KeychainProtectionParameter.TYPE_LOCKSCREEN;
-import static android.security.keystore.RecoveryMetadata.TYPE_PASSWORD;
-import static android.security.keystore.RecoveryMetadata.TYPE_PATTERN;
-import static android.security.keystore.RecoveryMetadata.TYPE_PIN;
+import static android.security.keystore.KeychainProtectionParameter.TYPE_PASSWORD;
+import static android.security.keystore.KeychainProtectionParameter.TYPE_PATTERN;
+import static android.security.keystore.KeychainProtectionParameter.TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
@@ -41,8 +41,8 @@
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyDerivationParams;
-import android.security.keystore.EntryRecoveryData;
-import android.security.keystore.RecoveryData;
+import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.WrappedApplicationKey;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -283,9 +283,9 @@
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
mKeySyncTask.run();
- RecoveryData recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
KeyDerivationParams KeyDerivationParams =
- recoveryData.getRecoveryMetadata().get(0).getKeyDerivationParams();
+ keychainSnapshot.getKeychainProtectionParams().get(0).getKeyDerivationParams();
assertThat(KeyDerivationParams.getAlgorithm()).isEqualTo(
KeyDerivationParams.ALGORITHM_SHA256);
verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
@@ -296,15 +296,15 @@
assertThat(counterId).isNotNull();
byte[] recoveryKey = decryptThmEncryptedKey(
lockScreenHash,
- recoveryData.getEncryptedRecoveryKeyBlob(),
+ keychainSnapshot.getEncryptedRecoveryKeyBlob(),
/*vaultParams=*/ KeySyncUtils.packVaultParams(
mKeyPair.getPublic(),
counterId,
TEST_DEVICE_ID,
/*maxAttempts=*/ 10));
- List<EntryRecoveryData> applicationKeys = recoveryData.getEntryRecoveryData();
+ List<WrappedApplicationKey> applicationKeys = keychainSnapshot.getWrappedApplicationKeys();
assertThat(applicationKeys).hasSize(1);
- EntryRecoveryData keyData = applicationKeys.get(0);
+ WrappedApplicationKey keyData = applicationKeys.get(0);
assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias());
assertThat(keyData.getAlias()).isEqualTo(keyData.getAlias());
byte[] appKey = KeySyncUtils.decryptApplicationKey(
@@ -322,14 +322,14 @@
mKeySyncTask.run();
- RecoveryData recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
- assertThat(recoveryData.getSnapshotVersion()).isEqualTo(1); // default value;
+ KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ assertThat(keychainSnapshot.getSnapshotVersion()).isEqualTo(1); // default value;
mRecoverableKeyStoreDb.setShouldCreateSnapshot(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, true);
mKeySyncTask.run();
- recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
- assertThat(recoveryData.getSnapshotVersion()).isEqualTo(2); // Updated
+ keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ assertThat(keychainSnapshot.getSnapshotVersion()).isEqualTo(2); // Updated
}
@Test
@@ -352,9 +352,9 @@
mKeySyncTask.run();
- RecoveryData recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
- assertThat(recoveryData.getRecoveryMetadata()).hasSize(1);
- assertThat(recoveryData.getRecoveryMetadata().get(0).getLockScreenUiFormat()).
+ KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ assertThat(keychainSnapshot.getKeychainProtectionParams()).hasSize(1);
+ assertThat(keychainSnapshot.getKeychainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(TYPE_PASSWORD);
}
@@ -378,10 +378,10 @@
mKeySyncTask.run();
- RecoveryData recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
- assertThat(recoveryData.getRecoveryMetadata()).hasSize(1);
+ KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ assertThat(keychainSnapshot.getKeychainProtectionParams()).hasSize(1);
// Password with only digits is changed to pin.
- assertThat(recoveryData.getRecoveryMetadata().get(0).getLockScreenUiFormat()).
+ assertThat(keychainSnapshot.getKeychainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(TYPE_PIN);
}
@@ -405,9 +405,9 @@
mKeySyncTask.run();
- RecoveryData recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
- assertThat(recoveryData.getRecoveryMetadata()).hasSize(1);
- assertThat(recoveryData.getRecoveryMetadata().get(0).getLockScreenUiFormat()).
+ KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ assertThat(keychainSnapshot.getKeychainProtectionParams()).hasSize(1);
+ assertThat(keychainSnapshot.getKeychainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(TYPE_PATTERN);
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 1bdcf47..3715742 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -16,8 +16,8 @@
package com.android.server.locksettings.recoverablekeystore;
-import static android.security.keystore.RecoveryMetadata.TYPE_LOCKSCREEN;
-import static android.security.keystore.RecoveryMetadata.TYPE_PASSWORD;
+import static android.security.keystore.KeychainProtectionParameter.TYPE_LOCKSCREEN;
+import static android.security.keystore.KeychainProtectionParameter.TYPE_PASSWORD;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertArrayEquals;
@@ -43,9 +43,8 @@
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyDerivationParams;
-import android.security.keystore.EntryRecoveryData;
-import android.security.keystore.RecoveryMetadata;
-import android.security.keystore.RecoveryManager;
+import android.security.keystore.KeychainProtectionParameter;
+import android.security.keystore.WrappedApplicationKey;
import android.support.test.filters.SmallTest;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
@@ -251,7 +250,7 @@
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
ImmutableList.of(
- new RecoveryMetadata(
+ new KeychainProtectionParameter(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
KeyDerivationParams.createSha256Params(TEST_SALT),
@@ -270,7 +269,7 @@
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
ImmutableList.of(
- new RecoveryMetadata(
+ new KeychainProtectionParameter(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
KeyDerivationParams.createSha256Params(TEST_SALT),
@@ -295,7 +294,7 @@
fail("should have thrown");
} catch (ServiceSpecificException e) {
assertThat(e.getMessage()).startsWith(
- "Only a single RecoveryMetadata is supported");
+ "Only a single KeychainProtectionParameter is supported");
}
}
@@ -308,7 +307,7 @@
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
ImmutableList.of(
- new RecoveryMetadata(
+ new KeychainProtectionParameter(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
KeyDerivationParams.createSha256Params(TEST_SALT),
@@ -330,7 +329,7 @@
vaultParams,
TEST_VAULT_CHALLENGE,
ImmutableList.of(
- new RecoveryMetadata(
+ new KeychainProtectionParameter(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
KeyDerivationParams.createSha256Params(TEST_SALT),
@@ -348,7 +347,7 @@
TEST_SESSION_ID,
/*recoveryKeyBlob=*/ randomBytes(32),
/*applicationKeys=*/ ImmutableList.of(
- new EntryRecoveryData("alias", randomBytes(32))
+ new WrappedApplicationKey("alias", randomBytes(32))
));
fail("should have thrown");
} catch (ServiceSpecificException e) {
@@ -363,7 +362,7 @@
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new RecoveryMetadata(
+ ImmutableList.of(new KeychainProtectionParameter(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
KeyDerivationParams.createSha256Params(TEST_SALT),
@@ -387,7 +386,7 @@
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new RecoveryMetadata(
+ ImmutableList.of(new KeychainProtectionParameter(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
KeyDerivationParams.createSha256Params(TEST_SALT),
@@ -397,7 +396,7 @@
SecretKey recoveryKey = randomRecoveryKey();
byte[] encryptedClaimResponse = encryptClaimResponse(
keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
- EntryRecoveryData badApplicationKey = new EntryRecoveryData(
+ WrappedApplicationKey badApplicationKey = new WrappedApplicationKey(
TEST_ALIAS,
randomBytes(32));
@@ -419,7 +418,7 @@
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new RecoveryMetadata(
+ ImmutableList.of(new KeychainProtectionParameter(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
KeyDerivationParams.createSha256Params(TEST_SALT),
@@ -430,7 +429,7 @@
byte[] encryptedClaimResponse = encryptClaimResponse(
keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
byte[] applicationKeyBytes = randomBytes(32);
- EntryRecoveryData applicationKey = new EntryRecoveryData(
+ WrappedApplicationKey applicationKey = new WrappedApplicationKey(
TEST_ALIAS,
encryptedApplicationKey(recoveryKey, applicationKeyBytes));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
index 6308f74..56b44e2 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
@@ -3,7 +3,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
-import android.security.keystore.RecoveryData;
+import android.security.keystore.KeychainSnapshot;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -26,25 +26,25 @@
@Test
public void get_returnsSetSnapshot() {
int userId = 1000;
- RecoveryData recoveryData = new RecoveryData(
+ KeychainSnapshot keychainSnapshot = new KeychainSnapshot(
/*snapshotVersion=*/ 1,
new ArrayList<>(),
new ArrayList<>(),
new byte[0]);
- mRecoverySnapshotStorage.put(userId, recoveryData);
+ mRecoverySnapshotStorage.put(userId, keychainSnapshot);
- assertEquals(recoveryData, mRecoverySnapshotStorage.get(userId));
+ assertEquals(keychainSnapshot, mRecoverySnapshotStorage.get(userId));
}
@Test
public void remove_removesSnapshots() {
int userId = 1000;
- RecoveryData recoveryData = new RecoveryData(
+ KeychainSnapshot keychainSnapshot = new KeychainSnapshot(
/*snapshotVersion=*/ 1,
new ArrayList<>(),
new ArrayList<>(),
new byte[0]);
- mRecoverySnapshotStorage.put(userId, recoveryData);
+ mRecoverySnapshotStorage.put(userId, keychainSnapshot);
mRecoverySnapshotStorage.remove(userId);
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
new file mode 100644
index 0000000..897be34
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.platform.test.annotations.Postsubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * atest FrameworksServicesTests:com.android.server.wm.RemoteAnimationControllerTest
+ */
+@SmallTest
+@FlakyTest(detail = "Promote to presubmit if non-flakyness is established")
+@RunWith(AndroidJUnit4.class)
+public class RemoteAnimationControllerTest extends WindowTestsBase {
+
+ @Mock SurfaceControl mMockLeash;
+ @Mock Transaction mMockTransaction;
+ @Mock OnAnimationFinishedCallback mFinishedCallback;
+ @Mock IRemoteAnimationRunner mMockRunner;
+ private RemoteAnimationAdapter mAdapter;
+ private RemoteAnimationController mController;
+ private final OffsettableClock mClock = new OffsettableClock.Stopped();
+ private TestHandler mHandler;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50);
+ sWm.mH.runWithScissors(() -> {
+ mHandler = new TestHandler(null, mClock);
+ }, 0);
+ mController = new RemoteAnimationController(sWm, mAdapter, mHandler);
+ }
+
+ @Test
+ public void testRun() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ sWm.mOpeningApps.add(win.mAppToken);
+ try {
+ final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture());
+ assertEquals(1, appsCaptor.getValue().length);
+ final RemoteAnimationTarget app = appsCaptor.getValue()[0];
+ assertEquals(new Point(50, 100), app.position);
+ assertEquals(new Rect(50, 100, 150, 150), app.sourceContainerBounds);
+ assertEquals(win.mAppToken.getPrefixOrderIndex(), app.prefixOrderIndex);
+ assertEquals(win.mAppToken.getTask().mTaskId, app.taskId);
+ assertEquals(mMockLeash, app.leash);
+ assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect);
+ assertEquals(false, app.isTranslucent);
+ verify(mMockTransaction).setLayer(mMockLeash, app.prefixOrderIndex);
+ verify(mMockTransaction).setPosition(mMockLeash, app.position.x, app.position.y);
+ verify(mMockTransaction).setWindowCrop(mMockLeash, new Rect(0, 0, 100, 50));
+
+ finishedCaptor.getValue().onAnimationFinished();
+ verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+ } finally {
+ sWm.mOpeningApps.clear();
+ }
+ }
+
+ @Test
+ public void testCancel() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+
+ adapter.onAnimationCancelled(mMockLeash);
+ verify(mMockRunner).onAnimationCancelled();
+ }
+
+ @Test
+ public void testTimeout() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+
+ mClock.fastForward(2500);
+ mHandler.timeAdvance();
+
+ verify(mMockRunner).onAnimationCancelled();
+ verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 7be203a..6a4710b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -223,6 +223,19 @@
assertFalse(app.canAffectSystemUiFlags());
}
+ @Test
+ public void testIsSelfOrAncestorWindowAnimating() throws Exception {
+ final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
+ final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1");
+ final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2");
+ assertFalse(child2.isSelfOrAncestorWindowAnimatingExit());
+ child2.mAnimatingExit = true;
+ assertTrue(child2.isSelfOrAncestorWindowAnimatingExit());
+ child2.mAnimatingExit = false;
+ root.mAnimatingExit = true;
+ assertTrue(child2.isSelfOrAncestorWindowAnimatingExit());
+ }
+
private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) {
final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
new file mode 100644
index 0000000..689c2ce
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.os.UserManager;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.android.server.UiServiceTestCase;
+import com.android.server.notification.NotificationManagerService.NotificationAssistants;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class NotificationAssistantsTest extends UiServiceTestCase {
+
+ @Mock
+ private PackageManager mPm;
+ @Mock
+ private IPackageManager miPm;
+ @Mock
+ private UserManager mUm;
+ @Mock
+ NotificationManagerService mNm;
+
+ NotificationAssistants mAssistants;
+
+ @Mock
+ private ManagedServices.UserProfiles mUserProfiles;
+
+ Object mLock = new Object();
+
+ UserInfo mZero = new UserInfo(0, "zero", 0);
+ UserInfo mTen = new UserInfo(10, "ten", 0);
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ getContext().setMockPackageManager(mPm);
+ getContext().addMockSystemService(Context.USER_SERVICE, mUm);
+ mAssistants = spy(mNm.new NotificationAssistants(getContext(), mLock, mUserProfiles, miPm));
+
+ List<ResolveInfo> approved = new ArrayList<>();
+ ResolveInfo resolve = new ResolveInfo();
+ approved.add(resolve);
+ ServiceInfo info = new ServiceInfo();
+ info.packageName = "a";
+ info.name="a";
+ resolve.serviceInfo = info;
+ when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt()))
+ .thenReturn(approved);
+
+ List<UserInfo> users = new ArrayList<>();
+ users.add(mZero);
+ users.add(mTen);
+ users.add(new UserInfo(11, "11", 0));
+ users.add(new UserInfo(12, "12", 0));
+ for (UserInfo user : users) {
+ when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
+ }
+ when(mUm.getUsers()).thenReturn(users);
+ when(mUm.getUsers(anyBoolean())).thenReturn(users);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(new int[] {0, 10, 11, 12});
+ }
+
+ @Test
+ public void testXmlUpgrade() throws Exception {
+ String xml = "<enabled_assistants/>";
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ parser.nextTag();
+ mAssistants.readXml(parser);
+
+ //once per user
+ verify(mNm, times(mUm.getUsers().size())).readDefaultAssistant(anyInt());
+ }
+
+ @Test
+ public void testXmlUpgradeExistingApprovedComponents() throws Exception {
+ String xml = "<enabled_assistants>"
+ + "<service_listing approved=\"b/b\" user=\"10\" primary=\"true\" />"
+ + "</enabled_assistants>";
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ parser.nextTag();
+ mAssistants.readXml(parser);
+
+ // once per user
+ verify(mNm, times(mUm.getUsers().size())).readDefaultAssistant(anyInt());
+ verify(mAssistants, times(1)).addApprovedList(
+ new ComponentName("b", "b").flattenToString(),10, true);
+ }
+
+ @Test
+ public void testXmlUpgradeOnce() throws Exception {
+ String xml = "<enabled_assistants/>";
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ parser.nextTag();
+ mAssistants.readXml(parser);
+
+ XmlSerializer serializer = new FastXmlSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ mAssistants.writeXml(serializer, true);
+ serializer.endDocument();
+ serializer.flush();
+
+ //once per user
+ verify(mNm, times(mUm.getUsers().size())).readDefaultAssistant(anyInt());
+
+ Mockito.reset(mNm);
+
+ parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ parser.nextTag();
+ mAssistants.readXml(parser);
+
+ //once per user
+ verify(mNm, never()).readDefaultAssistant(anyInt());
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
index 9564ab9..36136a8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -48,7 +48,6 @@
mScheduleCalendar = new ScheduleCalendar();
mScheduleInfo = new ZenModeConfig.ScheduleInfo();
mScheduleInfo.days = new int[] {1, 2, 3, 4, 5};
- mScheduleCalendar.setSchedule(mScheduleInfo);
}
@Test
@@ -100,6 +99,7 @@
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
mScheduleInfo.exitAtAlarm = false;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
Calendar expected = new GregorianCalendar();
expected.setTimeInMillis(cal.getTimeInMillis());
@@ -126,6 +126,7 @@
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
mScheduleInfo.exitAtAlarm = false;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
Calendar expected = new GregorianCalendar();
expected.setTimeInMillis(cal.getTimeInMillis());
@@ -153,6 +154,7 @@
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
mScheduleInfo.exitAtAlarm = false;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
Calendar expected = new GregorianCalendar();
expected.setTimeInMillis(cal.getTimeInMillis());
@@ -171,6 +173,7 @@
public void testShouldExitForAlarm_settingOff() {
mScheduleInfo.exitAtAlarm = false;
mScheduleInfo.nextAlarm = 1000;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertFalse(mScheduleCalendar.shouldExitForAlarm(1000));
}
@@ -179,6 +182,7 @@
public void testShouldExitForAlarm_beforeAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 1000;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertFalse(mScheduleCalendar.shouldExitForAlarm(999));
}
@@ -187,6 +191,7 @@
public void testShouldExitForAlarm_noAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 0;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertFalse(mScheduleCalendar.shouldExitForAlarm(999));
}
@@ -195,6 +200,7 @@
public void testShouldExitForAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 1000;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertTrue(mScheduleCalendar.shouldExitForAlarm(1000));
}
@@ -203,6 +209,7 @@
public void testMaybeSetNextAlarm_settingOff() {
mScheduleInfo.exitAtAlarm = false;
mScheduleInfo.nextAlarm = 0;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleCalendar.maybeSetNextAlarm(1000, 2000);
@@ -213,6 +220,7 @@
public void testMaybeSetNextAlarm_settingOn() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 0;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleCalendar.maybeSetNextAlarm(1000, 2000);
@@ -223,6 +231,7 @@
public void testMaybeSetNextAlarm_alarmCanceled() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 10000;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleCalendar.maybeSetNextAlarm(1000, 0);
@@ -233,6 +242,7 @@
public void testMaybeSetNextAlarm_earlierAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 2000;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleCalendar.maybeSetNextAlarm(1000, 1500);
@@ -242,6 +252,7 @@
@Test
public void testMaybeSetNextAlarm_laterAlarm() {
mScheduleInfo.exitAtAlarm = true;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleInfo.nextAlarm = 2000;
mScheduleCalendar.maybeSetNextAlarm(1000, 3000);
@@ -253,6 +264,7 @@
public void testMaybeSetNextAlarm_expiredAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 998;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleCalendar.maybeSetNextAlarm(1000, 999);
@@ -272,6 +284,7 @@
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertTrue(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
@@ -289,6 +302,7 @@
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 16;
mScheduleInfo.endMinute = 15;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertTrue(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
@@ -306,6 +320,7 @@
mScheduleInfo.startMinute = 16;
mScheduleInfo.endHour = 15;
mScheduleInfo.endMinute = 15;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertFalse(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
@@ -322,6 +337,7 @@
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 16;
mScheduleInfo.endMinute = 15;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertFalse(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 1ddab5b..4fbb228 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -353,7 +353,7 @@
@Test
public void testRemoveTransportModeTransform() throws Exception {
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
- mIpSecService.removeTransportModeTransforms(pfd, 1);
+ mIpSecService.removeTransportModeTransforms(pfd);
verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
}
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index b2a27e8..3eba881 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -423,7 +423,7 @@
@Test
public void testRemoveTransportModeTransform() throws Exception {
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
- mIpSecService.removeTransportModeTransforms(pfd, 1);
+ mIpSecService.removeTransportModeTransforms(pfd);
verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
}
diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp
index b692ccf..f5f5b05 100644
--- a/tools/aapt2/java/ClassDefinition.cpp
+++ b/tools/aapt2/java/ClassDefinition.cpp
@@ -45,8 +45,18 @@
Result result = Result::kAdded;
auto iter = indexed_members_.find(member->GetName());
if (iter != indexed_members_.end()) {
- // Overwrite the entry.
- ordered_members_[iter->second].reset();
+ // Overwrite the entry. Be careful, as the key in indexed_members_ is actually memory owned
+ // by the value at ordered_members_[index]. Since overwriting a value for a key doesn't replace
+ // the key (the initial key inserted into the unordered_map is kept), we must erase and then
+ // insert a new key, whose memory is being kept around. We do all this to avoid using more
+ // memory for each key.
+ size_t index = iter->second;
+
+ // Erase the key + value from the map.
+ indexed_members_.erase(iter);
+
+ // Now clear the memory that was backing the key (now erased).
+ ordered_members_[index].reset();
result = Result::kOverridden;
}
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
index c324238..f4e10ab 100644
--- a/tools/aapt2/java/ManifestClassGenerator_test.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -129,13 +129,16 @@
std::unique_ptr<xml::XmlResource> manifest = test::BuildXmlDom(R"(
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<permission android:name="android.permission.ACCESS_INTERNET" />
- <permission android:name="com.android.aapt.test.ACCESS_INTERNET" />
+ <permission android:name="com.android.sample.ACCESS_INTERNET" />
+ <permission android:name="com.android.permission.UNRELATED_PERMISSION" />
+ <permission android:name="com.android.aapt.test.ACCESS_INTERNET" /> -->
</manifest>)");
std::string actual;
ASSERT_TRUE(GetManifestClassText(context.get(), manifest.get(), &actual));
EXPECT_THAT(actual, HasSubstr("ACCESS_INTERNET=\"com.android.aapt.test.ACCESS_INTERNET\";"));
EXPECT_THAT(actual, Not(HasSubstr("ACCESS_INTERNET=\"android.permission.ACCESS_INTERNET\";")));
+ EXPECT_THAT(actual, Not(HasSubstr("ACCESS_INTERNET=\"com.android.sample.ACCESS_INTERNET\";")));
}
static ::testing::AssertionResult GetManifestClassText(IAaptContext* context, xml::XmlResource* res,