Merge "Restore calling identity before checking permission" into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index fac1d8c..64d6c79 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5454,6 +5454,7 @@
method public void onFullBackup(android.app.backup.FullBackupDataOutput) throws java.io.IOException;
method public abstract void onRestore(android.app.backup.BackupDataInput, int, android.os.ParcelFileDescriptor) throws java.io.IOException;
method public void onRestoreFile(android.os.ParcelFileDescriptor, long, java.io.File, int, long, long) throws java.io.IOException;
+ method public void onRestoreFinished();
field public static final int TYPE_DIRECTORY = 2; // 0x2
field public static final int TYPE_FILE = 1; // 0x1
}
@@ -8683,9 +8684,13 @@
field public static final java.lang.String FEATURE_CAMERA = "android.hardware.camera";
field public static final java.lang.String FEATURE_CAMERA_ANY = "android.hardware.camera.any";
field public static final java.lang.String FEATURE_CAMERA_AUTOFOCUS = "android.hardware.camera.autofocus";
+ field public static final java.lang.String FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING = "android.hardware.camera.capability.manual_post_processing";
+ field public static final java.lang.String FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR = "android.hardware.camera.capability.manual_sensor";
+ field public static final java.lang.String FEATURE_CAMERA_CAPABILITY_RAW = "android.hardware.camera.capability.raw";
field public static final java.lang.String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external";
field public static final java.lang.String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash";
field public static final java.lang.String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
+ field public static final java.lang.String FEATURE_CAMERA_LEVEL_FULL = "android.hardware.camera.level.full";
field public static final java.lang.String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
@@ -12763,9 +12768,9 @@
field public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; // 0x2
field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
field public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; // 0x0
- field public static final int REQUEST_AVAILABLE_CAPABILITIES_DNG = 3; // 0x3
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
+ field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR = 3; // 0x3
@@ -22310,9 +22315,10 @@
field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";
field public static final java.lang.String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
field public static final java.lang.String DISALLOW_MOUNT_PHYSICAL_MEDIA = "no_physical_media";
+ field public static final java.lang.String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user";
field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
- field public static final java.lang.String DISALLOW_TELEPHONY = "no_telephony";
+ field public static final java.lang.String DISALLOW_SMS = "no_sms";
field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
@@ -33523,6 +33529,7 @@
method public boolean dispatchKeyEventPreIme(android.view.KeyEvent);
method public boolean dispatchKeyShortcutEvent(android.view.KeyEvent);
method public boolean dispatchNestedFling(float, float, boolean);
+ method public boolean dispatchNestedPreFling(float, float);
method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
method public boolean dispatchNestedScroll(int, int, int, int, int[]);
method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
@@ -34333,6 +34340,7 @@
method public boolean onInterceptTouchEvent(android.view.MotionEvent);
method protected abstract void onLayout(boolean, int, int, int, int);
method public boolean onNestedFling(android.view.View, float, float, boolean);
+ method public boolean onNestedPreFling(android.view.View, float, float);
method public void onNestedPreScroll(android.view.View, int, int, int[]);
method public void onNestedScroll(android.view.View, int, int, int, int);
method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
@@ -34478,6 +34486,7 @@
method public abstract boolean isTextDirectionResolved();
method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
+ method public abstract boolean onNestedPreFling(android.view.View, float, float);
method public abstract void onNestedPreScroll(android.view.View, int, int, int[]);
method public abstract void onNestedScroll(android.view.View, int, int, int, int);
method public abstract void onNestedScrollAccepted(android.view.View, android.view.View, int);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 15f3a75..48954f4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2610,15 +2610,7 @@
return;
}
- if (mBackupAgents.get(packageName) != null) {
- Slog.d(TAG, "BackupAgent " + " for " + packageName
- + " already exists");
- return;
- }
-
- BackupAgent agent = null;
String classname = data.appInfo.backupAgentName;
-
// full backup operation but no app-supplied agent? use the default implementation
if (classname == null && (data.backupMode == IApplicationThread.BACKUP_MODE_FULL
|| data.backupMode == IApplicationThread.BACKUP_MODE_RESTORE_FULL)) {
@@ -2627,29 +2619,38 @@
try {
IBinder binder = null;
- try {
- if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
-
- java.lang.ClassLoader cl = packageInfo.getClassLoader();
- agent = (BackupAgent) cl.loadClass(classname).newInstance();
-
- // set up the agent's context
- ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
- context.setOuterContext(agent);
- agent.attach(context);
-
- agent.onCreate();
- binder = agent.onBind();
- mBackupAgents.put(packageName, agent);
- } catch (Exception e) {
- // If this is during restore, fail silently; otherwise go
- // ahead and let the user see the crash.
- Slog.e(TAG, "Agent threw during creation: " + e);
- if (data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE
- && data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE_FULL) {
- throw e;
+ BackupAgent agent = mBackupAgents.get(packageName);
+ if (agent != null) {
+ // reusing the existing instance
+ if (DEBUG_BACKUP) {
+ Slog.v(TAG, "Reusing existing agent instance");
}
- // falling through with 'binder' still null
+ binder = agent.onBind();
+ } else {
+ try {
+ if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
+
+ java.lang.ClassLoader cl = packageInfo.getClassLoader();
+ agent = (BackupAgent) cl.loadClass(classname).newInstance();
+
+ // set up the agent's context
+ ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
+ context.setOuterContext(agent);
+ agent.attach(context);
+
+ agent.onCreate();
+ binder = agent.onBind();
+ mBackupAgents.put(packageName, agent);
+ } catch (Exception e) {
+ // If this is during restore, fail silently; otherwise go
+ // ahead and let the user see the crash.
+ Slog.e(TAG, "Agent threw during creation: " + e);
+ if (data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE
+ && data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE_FULL) {
+ throw e;
+ }
+ // falling through with 'binder' still null
+ }
}
// tell the OS that we're live now
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index 7036aea..451af99 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -125,6 +125,21 @@
int token, IBackupManager callbackBinder);
/**
+ * Provide the app with a canonical "all data has been delivered" end-of-restore
+ * callback so that it can do any postprocessing of the restored data that might
+ * be appropriate. This is issued after both key/value and full data restore
+ * operations have completed.
+ *
+ * @param token Opaque token identifying this transaction. This must
+ * be echoed back to the backup service binder once the agent is
+ * finished restoring the application based on the restore data
+ * contents.
+ * @param callbackBinder Binder on which to indicate operation completion,
+ * passed here as a convenience to the agent.
+ */
+ void doRestoreFinished(int token, IBackupManager callbackBinder);
+
+ /**
* Out of band: instruct the agent to crash within the client process. This is used
* when the backup infrastructure detects a semantic error post-hoc and needs to
* pass the problem back to the app.
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index e2a86e8..87d785a 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -209,7 +209,7 @@
* output stream.
*/
public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
- ParcelFileDescriptor newState) throws IOException;
+ ParcelFileDescriptor newState) throws IOException;
/**
* The application is being restored from backup and should replace any
@@ -243,8 +243,7 @@
* When a full-backup dataset is being restored, this will be <code>null</code>.
*/
public abstract void onRestore(BackupDataInput data, int appVersionCode,
- ParcelFileDescriptor newState)
- throws IOException;
+ ParcelFileDescriptor newState) throws IOException;
/**
* The application is having its entire file system contents backed up. {@code data}
@@ -575,6 +574,20 @@
FullBackup.restoreFile(data, size, type, mode, mtime, null);
}
+ /**
+ * The application's restore operation has completed. This method is called after
+ * all available data has been delivered to the application for restore (via either
+ * the {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} or
+ * {@link #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long) onRestoreFile()}
+ * callbacks). This provides the app with a stable end-of-restore opportunity to
+ * perform any appropriate post-processing on the data that was just delivered.
+ *
+ * @see #onRestore(BackupDataInput, int, ParcelFileDescriptor)
+ * @see #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long)
+ */
+ public void onRestoreFinished() {
+ }
+
// ----- Core implementation -----
/** @hide */
@@ -723,6 +736,24 @@
}
@Override
+ public void doRestoreFinished(int token, IBackupManager callbackBinder) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ BackupAgent.this.onRestoreFinished();
+ } finally {
+ // Ensure that any side-effect SharedPreferences writes have landed
+ waitForSharedPrefs();
+
+ Binder.restoreCallingIdentity(ident);
+ try {
+ callbackBinder.opComplete(token);
+ } catch (RemoteException e) {
+ // we'll time out anyway, so we're safe
+ }
+ }
+ }
+
+ @Override
public void fail(String message) {
getHandler().post(new FailRunnable(message));
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 37df29a..ee7810d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1033,6 +1033,45 @@
public static final String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+ * of the cameras on the device supports the
+ * {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL full hardware}
+ * capability level.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_CAMERA_LEVEL_FULL = "android.hardware.camera.level.full";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+ * of the cameras on the device supports the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR manual sensor}
+ * capability level.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR =
+ "android.hardware.camera.capability.manual_sensor";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+ * of the cameras on the device supports the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING manual post-processing}
+ * capability level.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING =
+ "android.hardware.camera.capability.manual_post_processing";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+ * of the cameras on the device supports the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}
+ * capability level.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_CAMERA_CAPABILITY_RAW =
+ "android.hardware.camera.capability.raw";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device is capable of communicating with
* consumer IR devices.
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 4976a48..ff86120 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -887,7 +887,7 @@
* @see #REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
* @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR
* @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING
- * @see #REQUEST_AVAILABLE_CAPABILITIES_DNG
+ * @see #REQUEST_AVAILABLE_CAPABILITIES_RAW
*/
public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
new Key<int[]>("android.request.availableCapabilities", int[].class);
@@ -1068,17 +1068,17 @@
* <tr>
* <td align="left">RAW_OPAQUE</td>
* <td align="left">RAW16</td>
- * <td align="left">DNG</td>
+ * <td align="left">RAW</td>
* </tr>
* <tr>
* <td align="left">RAW16</td>
* <td align="left">YUV_420_888</td>
- * <td align="left">DNG</td>
+ * <td align="left">RAW</td>
* </tr>
* <tr>
* <td align="left">RAW16</td>
* <td align="left">JPEG</td>
- * <td align="left">DNG</td>
+ * <td align="left">RAW</td>
* </tr>
* </tbody>
* </table>
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 36b1089..63130a7 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -387,8 +387,11 @@
public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2;
/**
- * <p>The camera device supports outputting RAW buffers that can be
- * saved offline into a DNG format.</p>
+ * <p>The camera device supports outputting RAW buffers and
+ * metadata for interpreting them.</p>
+ * <p>Devices supporting the RAW capability allow both for
+ * saving DNG files, and for direct application processing of
+ * raw sensor images.</p>
* <ul>
* <li>RAW_SENSOR is supported as an output format.</li>
* <li>The maximum available resolution for RAW_SENSOR streams
@@ -403,7 +406,7 @@
* @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
*/
- public static final int REQUEST_AVAILABLE_CAPABILITIES_DNG = 3;
+ public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3;
/**
* <p>The camera device supports the Zero Shutter Lag use case.</p>
@@ -1186,8 +1189,7 @@
* image while recording video) use case.</p>
* <p>The camera device should take the highest-quality image
* possible (given the other settings) without disrupting the
- * frame rate of video recording.<br />
- * </p>
+ * frame rate of video recording. </p>
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
*/
public static final int CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT = 4;
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index ebab563..ec1fc4a 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -475,7 +475,7 @@
@Override
@SuppressWarnings("unchecked")
public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
- return (T) metadata.getFaceRectangles();
+ return (T) metadata.getFaces();
}
});
sGetCommandMap.put(
@@ -483,7 +483,7 @@
@Override
@SuppressWarnings("unchecked")
public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
- return (T) metadata.getFaces();
+ return (T) metadata.getFaceRectangles();
}
});
sGetCommandMap.put(
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 7bbac2c..d3aee50 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -271,15 +271,26 @@
public static final String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
/**
- * Key for user restrictions. Specifies that the user is not allowed to send or receive
- * phone calls or text messages. Emergency calls may still be permitted.
+ * Key for user restrictions. Specifies that the user is not allowed to make outgoing
+ * phone calls. Emergency calls are still permitted.
* The default value is <code>false</code>.
* <p/>
* Type: Boolean
* @see #setUserRestrictions(Bundle)
* @see #getUserRestrictions()
*/
- public static final String DISALLOW_TELEPHONY = "no_telephony";
+ public static final String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
+
+ /**
+ * Key for user restrictions. Specifies that the user is not allowed to send or receive
+ * SMS messages.
+ * The default value is <code>false</code>.
+ * <p/>
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_SMS = "no_sms";
/**
* Key for user restrictions. Specifies that windows besides app windows should not be
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a3e2c96..7bc8bc5 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -18445,6 +18445,43 @@
}
/**
+ * Dispatch a fling to a nested scrolling parent before it is processed by this view.
+ *
+ * <p>Nested pre-fling events are to nested fling events what touch intercept is to touch
+ * and what nested pre-scroll is to nested scroll. <code>dispatchNestedPreFling</code>
+ * offsets an opportunity for the parent view in a nested fling to fully consume the fling
+ * before the child view consumes it. If this method returns <code>true</code>, a nested
+ * parent view consumed the fling and this view should not scroll as a result.</p>
+ *
+ * <p>For a better user experience, only one view in a nested scrolling chain should consume
+ * the fling at a time. If a parent view consumed the fling this method will return false.
+ * Custom view implementations should account for this in two ways:</p>
+ *
+ * <ul>
+ * <li>If a custom view is paged and needs to settle to a fixed page-point, do not
+ * call <code>dispatchNestedPreFling</code>; consume the fling and settle to a valid
+ * position regardless.</li>
+ * <li>If a nested parent does consume the fling, this view should not scroll at all,
+ * even to settle back to a valid idle position.</li>
+ * </ul>
+ *
+ * <p>Views should also not offer fling velocities to nested parent views along an axis
+ * where scrolling is not currently supported; a {@link android.widget.ScrollView ScrollView}
+ * should not offer a horizontal fling velocity to its parents since scrolling along that
+ * axis is not permitted and carrying velocity along that motion does not make sense.</p>
+ *
+ * @param velocityX Horizontal fling velocity in pixels per second
+ * @param velocityY Vertical fling velocity in pixels per second
+ * @return true if a nested scrolling parent consumed the fling
+ */
+ public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
+ if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
+ return mNestedScrollingParent.onNestedPreFling(this, velocityX, velocityY);
+ }
+ return false;
+ }
+
+ /**
* Gets a scale factor that determines the distance the view should scroll
* vertically in response to {@link MotionEvent#ACTION_SCROLL}.
* @return The vertical scroll scale factor.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index ac70066..04c8b0b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -6066,6 +6066,14 @@
}
/**
+ * @inheritDoc
+ */
+ @Override
+ public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
+ return false;
+ }
+
+ /**
* Return the current axes of nested scrolling for this ViewGroup.
*
* <p>A ViewGroup returning something other than {@link #SCROLL_AXIS_NONE} is currently
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 588b9cd..87a37f4 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -523,10 +523,32 @@
* parent instead. The parent may optionally consume the fling or observe a child fling.</p>
*
* @param target View that initiated the nested scroll
- * @param velocityX Horizontal velocity in pixels per second.
+ * @param velocityX Horizontal velocity in pixels per second
* @param velocityY Vertical velocity in pixels per second
* @param consumed true if the child consumed the fling, false otherwise
* @return true if this parent consumed or otherwise reacted to the fling
*/
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);
+
+ /**
+ * React to a nested fling before the target view consumes it.
+ *
+ * <p>This method siginfies that a nested scrolling child has detected a fling with the given
+ * velocity along each axis. Generally this means that a touch scroll has ended with a
+ * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
+ * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
+ * along a scrollable axis.</p>
+ *
+ * <p>If a nested scrolling parent is consuming motion as part of a
+ * {@link #onNestedPreScroll(View, int, int, int[]) pre-scroll}, it may be appropriate for
+ * it to also consume the pre-fling to complete that same motion. By returning
+ * <code>true</code> from this method, the parent indicates that the child should not
+ * fling its own internal content as well.</p>
+ *
+ * @param target View that initiated the nested scroll
+ * @param velocityX Horizontal velocity in pixels per second
+ * @param velocityY Vertical velocity in pixels per second
+ * @return true if this parent consumed the fling ahead of the target view
+ */
+ public boolean onNestedPreFling(View target, float velocityX, float velocityY);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a8d3f99..5cad7db 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1052,25 +1052,24 @@
}
private boolean collectViewAttributes() {
- final View.AttachInfo attachInfo = mAttachInfo;
- if (attachInfo.mRecomputeGlobalAttributes) {
+ if (mAttachInfo.mRecomputeGlobalAttributes) {
//Log.i(TAG, "Computing view hierarchy attributes!");
- attachInfo.mRecomputeGlobalAttributes = false;
- boolean oldScreenOn = attachInfo.mKeepScreenOn;
- attachInfo.mKeepScreenOn = false;
- attachInfo.mSystemUiVisibility = 0;
- attachInfo.mHasSystemUiListeners = false;
- mView.dispatchCollectViewAttributes(attachInfo, 0);
- attachInfo.mSystemUiVisibility &= ~attachInfo.mDisabledSystemUiVisibility;
+ mAttachInfo.mRecomputeGlobalAttributes = false;
+ boolean oldScreenOn = mAttachInfo.mKeepScreenOn;
+ mAttachInfo.mKeepScreenOn = false;
+ mAttachInfo.mSystemUiVisibility = 0;
+ mAttachInfo.mHasSystemUiListeners = false;
+ mView.dispatchCollectViewAttributes(mAttachInfo, 0);
+ mAttachInfo.mSystemUiVisibility &= ~mAttachInfo.mDisabledSystemUiVisibility;
WindowManager.LayoutParams params = mWindowAttributes;
- attachInfo.mSystemUiVisibility |= getImpliedSystemUiVisibility(params);
- if (attachInfo.mKeepScreenOn != oldScreenOn
- || attachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility
- || attachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) {
+ mAttachInfo.mSystemUiVisibility |= getImpliedSystemUiVisibility(params);
+ if (mAttachInfo.mKeepScreenOn != oldScreenOn
+ || mAttachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility
+ || mAttachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) {
applyKeepScreenOnFlag(params);
- params.subtreeSystemUiVisibility = attachInfo.mSystemUiVisibility;
- params.hasSystemUiListeners = attachInfo.mHasSystemUiListeners;
- mView.dispatchWindowSystemUiVisiblityChanged(attachInfo.mSystemUiVisibility);
+ params.subtreeSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
+ params.hasSystemUiListeners = mAttachInfo.mHasSystemUiListeners;
+ mView.dispatchWindowSystemUiVisiblityChanged(mAttachInfo.mSystemUiVisibility);
return true;
}
}
@@ -1162,8 +1161,7 @@
* @param m input matrix to modify
*/
void transformMatrixToGlobal(Matrix m) {
- final View.AttachInfo attachInfo = mAttachInfo;
- m.postTranslate(attachInfo.mWindowLeft, attachInfo.mWindowTop);
+ m.postTranslate(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
}
/**
@@ -1173,8 +1171,7 @@
* @param m input matrix to modify
*/
void transformMatrixToLocal(Matrix m) {
- final View.AttachInfo attachInfo = mAttachInfo;
- m.preTranslate(-attachInfo.mWindowLeft, -attachInfo.mWindowTop);
+ m.preTranslate(-mAttachInfo.mWindowLeft, -mAttachInfo.mWindowTop);
}
void dispatchApplyInsets(View host) {
@@ -1216,8 +1213,6 @@
int desiredWindowWidth;
int desiredWindowHeight;
- final View.AttachInfo attachInfo = mAttachInfo;
-
final int viewVisibility = getHostVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility
|| mNewSurfaceNeeded;
@@ -1266,10 +1261,10 @@
// We used to use the following condition to choose 32 bits drawing caches:
// PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888
// However, windows are now always 32 bits by default, so choose 32 bits
- attachInfo.mUse32BitDrawingCache = true;
- attachInfo.mHasWindowFocus = false;
- attachInfo.mWindowVisibility = viewVisibility;
- attachInfo.mRecomputeGlobalAttributes = false;
+ mAttachInfo.mUse32BitDrawingCache = true;
+ mAttachInfo.mHasWindowFocus = false;
+ mAttachInfo.mWindowVisibility = viewVisibility;
+ mAttachInfo.mRecomputeGlobalAttributes = false;
viewVisibilityChanged = false;
mLastConfiguration.setTo(host.getResources().getConfiguration());
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
@@ -1277,8 +1272,8 @@
if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
host.setLayoutDirection(mLastConfiguration.getLayoutDirection());
}
- host.dispatchAttachedToWindow(attachInfo, 0);
- attachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
+ host.dispatchAttachedToWindow(mAttachInfo, 0);
+ mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
dispatchApplyInsets(host);
//Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
@@ -1295,7 +1290,7 @@
}
if (viewVisibilityChanged) {
- attachInfo.mWindowVisibility = viewVisibility;
+ mAttachInfo.mWindowVisibility = viewVisibility;
host.dispatchWindowVisibilityChanged(viewVisibility);
if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
destroyHardwareResources();
@@ -1308,7 +1303,7 @@
}
// Execute enqueued actions on every traversal in case a detached view enqueued an action
- getRunQueue().executeActions(attachInfo.mHandler);
+ getRunQueue().executeActions(mAttachInfo.mHandler);
boolean insetsChanged = false;
@@ -1364,21 +1359,21 @@
if (collectViewAttributes()) {
params = lp;
}
- if (attachInfo.mForceReportNewAttributes) {
- attachInfo.mForceReportNewAttributes = false;
+ if (mAttachInfo.mForceReportNewAttributes) {
+ mAttachInfo.mForceReportNewAttributes = false;
params = lp;
}
- if (mFirst || attachInfo.mViewVisibilityChanged) {
- attachInfo.mViewVisibilityChanged = false;
+ if (mFirst || mAttachInfo.mViewVisibilityChanged) {
+ mAttachInfo.mViewVisibilityChanged = false;
int resizeMode = mSoftInputMode &
WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
// If we are in auto resize mode, then we need to determine
// what mode to use now.
if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
- final int N = attachInfo.mScrollContainers.size();
+ final int N = mAttachInfo.mScrollContainers.size();
for (int i=0; i<N; i++) {
- if (attachInfo.mScrollContainers.get(i).isShown()) {
+ if (mAttachInfo.mScrollContainers.get(i).isShown()) {
resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
}
}
@@ -1437,8 +1432,8 @@
// If there are no inset listeners remaining then we may still need to compute
// insets in case the old insets were non-empty and must be reset.
final boolean computesInternalInsets =
- attachInfo.mTreeObserver.hasComputeInternalInsetsListeners()
- || attachInfo.mHasNonEmptyGivenInternalInsets;
+ mAttachInfo.mTreeObserver.hasComputeInternalInsetsListeners()
+ || mAttachInfo.mHasNonEmptyGivenInternalInsets;
boolean insetsPending = false;
int relayoutResult = 0;
@@ -1659,8 +1654,8 @@
if (DEBUG_ORIENTATION) Log.v(
TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface);
- attachInfo.mWindowLeft = frame.left;
- attachInfo.mWindowTop = frame.top;
+ mAttachInfo.mWindowLeft = frame.left;
+ mAttachInfo.mWindowTop = frame.top;
// !!FIXME!! This next section handles the case where we did not get the
// window size we asked for. We should avoid this by getting a maximum size from
@@ -1799,20 +1794,20 @@
// true since we are comparing a not translated value to a translated one.
// This scenario is rare but we may want to fix that.
- final boolean windowMoved = (attachInfo.mWindowLeft != frame.left
- || attachInfo.mWindowTop != frame.top);
+ final boolean windowMoved = (mAttachInfo.mWindowLeft != frame.left
+ || mAttachInfo.mWindowTop != frame.top);
if (windowMoved) {
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWinFrame(frame);
}
- attachInfo.mWindowLeft = frame.left;
- attachInfo.mWindowTop = frame.top;
+ mAttachInfo.mWindowLeft = frame.left;
+ mAttachInfo.mWindowTop = frame.top;
}
}
final boolean didLayout = layoutRequested && !mStopped;
boolean triggerGlobalLayoutListener = didLayout
- || attachInfo.mRecomputeGlobalAttributes;
+ || mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
@@ -1851,18 +1846,18 @@
}
if (triggerGlobalLayoutListener) {
- attachInfo.mRecomputeGlobalAttributes = false;
- attachInfo.mTreeObserver.dispatchOnGlobalLayout();
+ mAttachInfo.mRecomputeGlobalAttributes = false;
+ mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
if (computesInternalInsets) {
// Clear the original insets.
- final ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
+ final ViewTreeObserver.InternalInsetsInfo insets = mAttachInfo.mGivenInternalInsets;
insets.reset();
// Compute new insets in place.
- attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
- attachInfo.mHasNonEmptyGivenInternalInsets = !insets.isEmpty();
+ mAttachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
+ mAttachInfo.mHasNonEmptyGivenInternalInsets = !insets.isEmpty();
// Tell the window manager.
if (insetsPending || !mLastGivenInsets.equals(insets)) {
@@ -1941,7 +1936,7 @@
mReportNextDraw = true;
}
- boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw() ||
+ boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() ||
viewVisibility != View.VISIBLE;
if (!cancelDraw && !newSurface) {
@@ -2379,10 +2374,9 @@
scrollToRectOrFocus(null, false);
- final AttachInfo attachInfo = mAttachInfo;
- if (attachInfo.mViewScrollChanged) {
- attachInfo.mViewScrollChanged = false;
- attachInfo.mTreeObserver.dispatchOnScrollChanged();
+ if (mAttachInfo.mViewScrollChanged) {
+ mAttachInfo.mViewScrollChanged = false;
+ mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
}
boolean animating = mScroller != null && mScroller.computeScrollOffset();
@@ -2397,8 +2391,8 @@
fullRedrawNeeded = true;
}
- final float appScale = attachInfo.mApplicationScale;
- final boolean scalingRequired = attachInfo.mScalingRequired;
+ final float appScale = mAttachInfo.mApplicationScale;
+ final boolean scalingRequired = mAttachInfo.mScalingRequired;
int resizeAlpha = 0;
if (mResizeBuffer != null) {
@@ -2427,7 +2421,7 @@
}
if (fullRedrawNeeded) {
- attachInfo.mIgnoreDirtyState = true;
+ mAttachInfo.mIgnoreDirtyState = true;
dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
}
@@ -2440,7 +2434,7 @@
appScale + ", width=" + mWidth + ", height=" + mHeight);
}
- attachInfo.mTreeObserver.dispatchOnDraw();
+ mAttachInfo.mTreeObserver.dispatchOnDraw();
int xOffset = 0;
int yOffset = curScrollY;
@@ -2455,7 +2449,7 @@
}
if (!dirty.isEmpty() || mIsAnimating) {
- if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) {
+ if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
// Draw with hardware renderer.
mIsAnimating = false;
if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) {
@@ -2468,7 +2462,7 @@
dirty.setEmpty();
mBlockResizeBuffer = false;
- attachInfo.mHardwareRenderer.draw(mView, attachInfo, this);
+ mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
} else {
// If we get here with a disabled & requested hardware renderer, something went
// wrong (an invalidate posted right before we destroyed the hardware surface
@@ -2478,12 +2472,12 @@
// Before we request a new frame we must however attempt to reinitiliaze the
// hardware renderer if it's in requested state. This would happen after an
// eglTerminate() for instance.
- if (attachInfo.mHardwareRenderer != null &&
- !attachInfo.mHardwareRenderer.isEnabled() &&
- attachInfo.mHardwareRenderer.isRequested()) {
+ if (mAttachInfo.mHardwareRenderer != null &&
+ !mAttachInfo.mHardwareRenderer.isEnabled() &&
+ mAttachInfo.mHardwareRenderer.isRequested()) {
try {
- attachInfo.mHardwareRenderer.initializeIfNeeded(
+ mAttachInfo.mHardwareRenderer.initializeIfNeeded(
mWidth, mHeight, mSurface, surfaceInsets);
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
@@ -2495,7 +2489,7 @@
return;
}
- if (!drawSoftware(surface, attachInfo, xOffset, yOffset, scalingRequired, dirty)) {
+ if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
@@ -2651,20 +2645,17 @@
}
private Drawable getAccessibilityFocusedDrawable() {
- if (mAttachInfo != null) {
- // Lazily load the accessibility focus drawable.
- if (mAttachInfo.mAccessibilityFocusDrawable == null) {
- TypedValue value = new TypedValue();
- final boolean resolved = mView.mContext.getTheme().resolveAttribute(
- R.attr.accessibilityFocusedDrawable, value, true);
- if (resolved) {
- mAttachInfo.mAccessibilityFocusDrawable =
+ // Lazily load the accessibility focus drawable.
+ if (mAttachInfo.mAccessibilityFocusDrawable == null) {
+ TypedValue value = new TypedValue();
+ final boolean resolved = mView.mContext.getTheme().resolveAttribute(
+ R.attr.accessibilityFocusedDrawable, value, true);
+ if (resolved) {
+ mAttachInfo.mAccessibilityFocusDrawable =
mView.mContext.getDrawable(value.resourceId);
- }
}
- return mAttachInfo.mAccessibilityFocusDrawable;
}
- return null;
+ return mAttachInfo.mAccessibilityFocusDrawable;
}
/**
@@ -2678,9 +2669,8 @@
}
boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
- final View.AttachInfo attachInfo = mAttachInfo;
- final Rect ci = attachInfo.mContentInsets;
- final Rect vi = attachInfo.mVisibleInsets;
+ final Rect ci = mAttachInfo.mContentInsets;
+ final Rect vi = mAttachInfo.mVisibleInsets;
int scrollY = 0;
boolean handled = false;
@@ -5147,12 +5137,11 @@
if (args.localChanges != 0) {
mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges);
}
- if (mAttachInfo != null) {
- int visibility = args.globalVisibility&View.SYSTEM_UI_CLEARABLE_FLAGS;
- if (visibility != mAttachInfo.mGlobalSystemUiVisibility) {
- mAttachInfo.mGlobalSystemUiVisibility = visibility;
- mView.dispatchSystemUiVisibilityChanged(visibility);
- }
+
+ int visibility = args.globalVisibility&View.SYSTEM_UI_CLEARABLE_FLAGS;
+ if (visibility != mAttachInfo.mGlobalSystemUiVisibility) {
+ mAttachInfo.mGlobalSystemUiVisibility = visibility;
+ mView.dispatchSystemUiVisibilityChanged(visibility);
}
}
@@ -5485,8 +5474,7 @@
}
private void destroyHardwareRenderer() {
- AttachInfo attachInfo = mAttachInfo;
- HardwareRenderer hardwareRenderer = attachInfo.mHardwareRenderer;
+ HardwareRenderer hardwareRenderer = mAttachInfo.mHardwareRenderer;
if (hardwareRenderer != null) {
if (mView != null) {
@@ -5495,8 +5483,8 @@
hardwareRenderer.destroy();
hardwareRenderer.setRequested(false);
- attachInfo.mHardwareRenderer = null;
- attachInfo.mHardwareAccelerated = false;
+ mAttachInfo.mHardwareRenderer = null;
+ mAttachInfo.mHardwareAccelerated = false;
}
}
@@ -6197,37 +6185,35 @@
}
private View getCommonPredecessor(View first, View second) {
- if (mAttachInfo != null) {
- if (mTempHashSet == null) {
- mTempHashSet = new HashSet<View>();
- }
- HashSet<View> seen = mTempHashSet;
- seen.clear();
- View firstCurrent = first;
- while (firstCurrent != null) {
- seen.add(firstCurrent);
- ViewParent firstCurrentParent = firstCurrent.mParent;
- if (firstCurrentParent instanceof View) {
- firstCurrent = (View) firstCurrentParent;
- } else {
- firstCurrent = null;
- }
- }
- View secondCurrent = second;
- while (secondCurrent != null) {
- if (seen.contains(secondCurrent)) {
- seen.clear();
- return secondCurrent;
- }
- ViewParent secondCurrentParent = secondCurrent.mParent;
- if (secondCurrentParent instanceof View) {
- secondCurrent = (View) secondCurrentParent;
- } else {
- secondCurrent = null;
- }
- }
- seen.clear();
+ if (mTempHashSet == null) {
+ mTempHashSet = new HashSet<View>();
}
+ HashSet<View> seen = mTempHashSet;
+ seen.clear();
+ View firstCurrent = first;
+ while (firstCurrent != null) {
+ seen.add(firstCurrent);
+ ViewParent firstCurrentParent = firstCurrent.mParent;
+ if (firstCurrentParent instanceof View) {
+ firstCurrent = (View) firstCurrentParent;
+ } else {
+ firstCurrent = null;
+ }
+ }
+ View secondCurrent = second;
+ while (secondCurrent != null) {
+ if (seen.contains(secondCurrent)) {
+ seen.clear();
+ return secondCurrent;
+ }
+ ViewParent secondCurrentParent = secondCurrent.mParent;
+ if (secondCurrentParent instanceof View) {
+ secondCurrent = (View) secondCurrentParent;
+ } else {
+ secondCurrent = null;
+ }
+ }
+ seen.clear();
return null;
}
@@ -6291,6 +6277,11 @@
return false;
}
+ @Override
+ public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
+ return false;
+ }
+
void changeCanvasOpacity(boolean opaque) {
Log.d(TAG, "changeCanvasOpacity: opaque=" + opaque);
if (mAttachInfo.mHardwareRenderer != null) {
@@ -6635,6 +6626,9 @@
// Destroy Displaylists so they can be recreated with high contrast recordings
destroyHardwareResources();
+
+ // Schedule redraw, which will rerecord + redraw all text
+ invalidate();
}
}
diff --git a/docs/html/preview/tv/adt-1/index.jd b/docs/html/preview/tv/adt-1/index.jd
index e09dcbf..882f421 100644
--- a/docs/html/preview/tv/adt-1/index.jd
+++ b/docs/html/preview/tv/adt-1/index.jd
@@ -139,15 +139,15 @@
<li>YouTube</li>
<li>Netflix</li>
<li>Google+ Photos</li>
- <li>Google Play Movies and TV (Android only)</li>
+ <li>Google Play Movies and TV (Android and iOS only)</li>
+ <li>Google Play Music</li>
+ <li>Mirror your Android device screen to ADT-1</li>
</ul>
<p>Coming soon:</p>
<ul>
- <li>Google Play Music</li>
- <li>Google Play Movies and TV (iOS and Chrome)</li>
- <li>Mirror you Android device screen to ADT-1</li>
+ <li>Google Play Movies and TV (Chrome)</li>
</ul>
<p class="note">
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 75d52b4..9f66904 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -21,6 +21,7 @@
#define LOG_TAG "OpenGLRenderer"
#endif
+#include <SkColor.h>
#include <SkPath.h>
#include <SkPathOps.h>
#include <SkXfermode.h>
@@ -1315,7 +1316,7 @@
FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
fontRenderer.precache(paint, mText, mCount, SkMatrix::I());
- deferInfo.batchId = mPaint->getColor() == 0xff000000 ?
+ deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ?
DeferredDisplayList::kOpBatch_Text :
DeferredDisplayList::kOpBatch_ColorText;
}
@@ -1385,17 +1386,19 @@
fontRenderer.precache(paint, mText, mCount, transform);
mPrecacheTransform = transform;
}
- deferInfo.batchId = mPaint->getColor() == 0xff000000 ?
+ deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ?
DeferredDisplayList::kOpBatch_Text :
DeferredDisplayList::kOpBatch_ColorText;
deferInfo.mergeId = reinterpret_cast<mergeid_t>(mPaint->getColor());
// don't merge decorated text - the decorations won't draw in order
- bool noDecorations = !(mPaint->getFlags() & (SkPaint::kUnderlineText_Flag |
- SkPaint::kStrikeThruText_Flag));
- deferInfo.mergeable = state.mMatrix.isPureTranslate() && noDecorations &&
- OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+ bool hasDecorations = mPaint->getFlags()
+ & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag);
+
+ deferInfo.mergeable = state.mMatrix.isPureTranslate()
+ && !hasDecorations
+ && OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 7fa1975..e468a75 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <SkCanvas.h>
+#include <SkColor.h>
#include <SkShader.h>
#include <SkTypeface.h>
@@ -1462,7 +1463,7 @@
mSkipOutlineClip = true;
SkPaint paint;
- paint.setColor(0xff000000);
+ paint.setColor(SK_ColorBLACK);
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
// NOTE: We could use the region contour path to generate a smaller mesh
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index ae48608..9ba8854 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -19,6 +19,7 @@
#include <SkBitmap.h>
#include <SkCanvas.h>
+#include <SkColor.h>
#include <SkPaint.h>
#include <SkPath.h>
#include <SkRect.h>
@@ -111,7 +112,7 @@
static void initPaint(SkPaint& paint) {
// Make sure the paint is opaque, color, alpha, filter, etc.
// will be applied later when compositing the alpha8 texture
- paint.setColor(0xff000000);
+ paint.setColor(SK_ColorBLACK);
paint.setAlpha(255);
paint.setColorFilter(NULL);
paint.setMaskFilter(NULL);
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index a4ac262..2147810 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -185,7 +185,7 @@
} else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
dirty.setEmpty();
} else {
- if (!dirty.intersect(0, 0, width, height)) {
+ if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) {
ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
SK_RECT_ARGS(dirty), width, height);
dirty.setEmpty();
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b7210e1..5752646 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -70,6 +70,7 @@
<uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
<uses-permission android:name="android.permission.SET_SCREEN_COMPATIBILITY" />
<uses-permission android:name="android.permission.START_ANY_ACTIVITY" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" />
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c8c8e9a..f8c9d4c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -703,4 +703,5 @@
<!-- Text shown in place of notification contents when the notification is hidden on a secure lockscreen -->
<string name="notification_hidden_text">Contents hidden</string>
+ <string name="guest_exit_guest">Exit guest</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index 3c647ed..67eef56 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -81,13 +81,7 @@
UserSwitcherController.UserRecord item = getItem(position);
UserDetailItemView v = UserDetailItemView.convertOrInflate(
mContext, convertView, parent);
- String name;
- if (item.isGuest) {
- name = mContext.getString(
- item.info == null ? R.string.guest_new_guest : R.string.guest_nickname);
- } else {
- name = item.info.name;
- }
+ String name = getName(mContext, item);
if (item.picture == null) {
v.bind(name, mContext.getDrawable(R.drawable.ic_account_circle_qs));
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 607e155..bd5df28 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -16,6 +16,7 @@
package com.android.systemui.recents.misc;
+import android.content.Intent;
import android.graphics.Color;
import android.graphics.Rect;
import com.android.systemui.recents.RecentsConfiguration;
@@ -93,4 +94,10 @@
throws IllegalAccessException, InvocationTargetException {
sPropertyMethod.invoke(null, property, value);
}
+
+ /** Returns whether the specified intent is a document. */
+ public static boolean isDocument(Intent intent) {
+ int flags = intent.getFlags();
+ return (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) == Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
index 5f4fabe..bb4dc76 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
@@ -28,20 +28,20 @@
public class KeyStoreLruCache<V> {
// We keep a set of keys that are associated with the LRU cache, so that we can find out
// information about the Task that was previously in the cache.
- HashMap<Task.TaskKey, Task.TaskKey> mKeys = new HashMap<Task.TaskKey, Task.TaskKey>();
+ HashMap<Integer, Task.TaskKey> mTaskKeys = new HashMap<Integer, Task.TaskKey>();
// The cache implementation
- LruCache<Task.TaskKey, V> mCache;
+ LruCache<Integer, V> mCache;
public KeyStoreLruCache(int cacheSize) {
- mCache = new LruCache<Task.TaskKey, V>(cacheSize) {
+ mCache = new LruCache<Integer, V>(cacheSize) {
@Override
- protected int sizeOf(Task.TaskKey t, V v) {
+ protected int sizeOf(Integer taskId, V v) {
return computeSize(v);
}
@Override
- protected void entryRemoved(boolean evicted, Task.TaskKey key, V oldV, V newV) {
- mKeys.remove(key);
+ protected void entryRemoved(boolean evicted, Integer taskId, V oldV, V newV) {
+ mTaskKeys.remove(taskId);
}
};
}
@@ -53,46 +53,41 @@
/** Gets a specific entry in the cache. */
final V get(Task.TaskKey key) {
- return mCache.get(key);
+ return mCache.get(key.id);
}
/**
- * Returns the value only if the last active time of the key currently in the lru cache is
- * greater than or equal to the last active time of the key specified.
+ * Returns the value only if the Task has not updated since the last time it was in the cache.
*/
- final V getCheckLastActiveTime(Task.TaskKey key) {
- Task.TaskKey lruKey = mKeys.get(key);
- if (lruKey != null && (lruKey.lastActiveTime < key.lastActiveTime)) {
- // The task has changed (been made active since the last time it was put into the
+ final V getAndInvalidateIfModified(Task.TaskKey key) {
+ Task.TaskKey lastKey = mTaskKeys.get(key.id);
+ if (lastKey != null && (lastKey.lastActiveTime < key.lastActiveTime)) {
+ // The task has updated (been made active since the last time it was put into the
// LRU cache) so invalidate that item in the cache
- remove(lruKey);
+ remove(key);
return null;
}
// Either the task does not exist in the cache, or the last active time is the same as
- // the key specified
- return mCache.get(key);
+ // the key specified, so return what is in the cache
+ return mCache.get(key.id);
}
/** Puts an entry in the cache for a specific key. */
final void put(Task.TaskKey key, V value) {
- mCache.put(key, value);
- if (mKeys.containsKey(key)) {
- mKeys.get(key).updateLastActiveTime(key.lastActiveTime);
- } else {
- mKeys.put(key, key);
- }
+ mCache.put(key.id, value);
+ mTaskKeys.put(key.id, key);
}
/** Removes a cache entry for a specific key. */
final void remove(Task.TaskKey key) {
- mCache.remove(key);
- mKeys.remove(key);
+ mCache.remove(key.id);
+ mTaskKeys.remove(key.id);
}
/** Removes all the entries in the cache. */
final void evictAll() {
mCache.evictAll();
- mKeys.clear();
+ mTaskKeys.clear();
}
/** Returns the size of the cache. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 71979c4..adf0794 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -31,6 +31,7 @@
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
@@ -42,6 +43,18 @@
ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
/** Adds a new task to the load queue */
+ void addTasks(Collection<Task> tasks) {
+ for (Task t : tasks) {
+ if (!mQueue.contains(t)) {
+ mQueue.add(t);
+ }
+ }
+ synchronized(this) {
+ notifyAll();
+ }
+ }
+
+ /** Adds a new task to the load queue */
void addTask(Task t) {
if (!mQueue.contains(t)) {
mQueue.add(t);
@@ -153,30 +166,30 @@
// Load the next item from the queue
final Task t = mLoadQueue.nextTask();
if (t != null) {
- Drawable cachedIcon = mApplicationIconCache.getCheckLastActiveTime(t.key);
- Bitmap cachedThumbnail = mThumbnailCache.getCheckLastActiveTime(t.key);
+ Drawable cachedIcon = mApplicationIconCache.get(t.key);
+ Bitmap cachedThumbnail = mThumbnailCache.get(t.key);
// Load the application icon if it is stale or we haven't cached one yet
if (cachedIcon == null) {
- Drawable icon = null;
ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent(),
t.userId);
if (info != null) {
- icon = ssp.getActivityIcon(info, t.userId);
+ cachedIcon = ssp.getActivityIcon(info, t.userId);
}
- // If we can't load the icon, then set the default application icon into the
- // cache. This will remain until the task's last active time is updated.
- cachedIcon = icon != null ? icon : mDefaultApplicationIcon;
+ if (cachedIcon == null) {
+ cachedIcon = mDefaultApplicationIcon;
+ }
+ // At this point, even if we can't load the icon, we will set the default
+ // icon.
mApplicationIconCache.put(t.key, cachedIcon);
}
// Load the thumbnail if it is stale or we haven't cached one yet
if (cachedThumbnail == null) {
- Bitmap thumbnail = ssp.getTaskThumbnail(t.key.id);
- if (thumbnail != null) {
- thumbnail.setHasAlpha(false);
+ cachedThumbnail = ssp.getTaskThumbnail(t.key.id);
+ if (cachedThumbnail != null) {
+ cachedThumbnail.setHasAlpha(false);
+ } else {
+ cachedThumbnail = mDefaultThumbnail;
}
- // Even if we can't load the icon, we set the default thumbnail into the
- // cache. This will remain until the task's last active time is updated.
- cachedThumbnail = thumbnail != null ? thumbnail : mDefaultThumbnail;
mThumbnailCache.put(t.key, cachedThumbnail);
}
if (!mCancelled) {
@@ -281,11 +294,11 @@
return mSystemServicesProxy;
}
+ /** Gets the list of recent tasks, ordered from back to front. */
private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp) {
List<ActivityManager.RecentTaskInfo> tasks =
ssp.getRecentTasks(50, UserHandle.CURRENT.getIdentifier());
Collections.reverse(tasks);
-
return tasks;
}
@@ -302,7 +315,7 @@
SystemServicesProxy ssp = mSystemServicesProxy;
List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
- // Add each task to the task stack
+ // From back to front, add each task to the task stack
int taskCount = tasks.size();
for (int i = 0; i < taskCount; i++) {
ActivityManager.RecentTaskInfo t = tasks.get(i);
@@ -311,7 +324,7 @@
ActivityManager.TaskDescription av = t.taskDescription;
String activityLabel = null;
- Drawable activityIcon = null;
+ Drawable activityIcon = mDefaultApplicationIcon;
int activityColor = config.taskBarViewDefaultBackgroundColor;
if (av != null) {
activityLabel = (av.getLabel() != null ? av.getLabel() : ssp.getActivityLabel(info));
@@ -323,7 +336,6 @@
} else {
activityLabel = ssp.getActivityLabel(info);
}
- boolean isForemostTask = (i == (taskCount - 1));
// Create a new task
Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, t.affiliatedTaskId,
@@ -333,44 +345,35 @@
// Preload the specified number of apps
if (i >= (taskCount - preloadCount)) {
// Load the icon from the cache if possible
- task.applicationIcon = mApplicationIconCache.getCheckLastActiveTime(task.key);
+ task.applicationIcon = mApplicationIconCache.getAndInvalidateIfModified(task.key);
if (task.applicationIcon == null) {
- if (isForemostTask) {
- // We force loading the application icon for the foremost task
- task.applicationIcon = ssp.getActivityIcon(info, task.userId);
- if (task.applicationIcon == null) {
- task.applicationIcon = mDefaultApplicationIcon;
- }
- // Even if we can't load the icon we set the default application icon into
- // the cache. This will remain until the task's last active time is updated.
+ // Load the icon from the system
+ task.applicationIcon = ssp.getActivityIcon(info, task.userId);
+ if (task.applicationIcon != null) {
mApplicationIconCache.put(task.key, task.applicationIcon);
- } else {
- // Either the task has changed since the last active time, or it was not
- // previously cached, so try and load the task anew.
- tasksToLoad.add(task);
}
}
+ if (task.applicationIcon == null) {
+ // Either the task has changed since the last active time, or it was not
+ // previously cached, so try and load the task anew.
+ tasksToLoad.add(task);
+ }
- // Load the thumbnail (if possible and not the foremost task, from the cache)
- task.thumbnail = mThumbnailCache.getCheckLastActiveTime(task.key);
+ // Load the thumbnail from the cache if possible
+ task.thumbnail = mThumbnailCache.getAndInvalidateIfModified(task.key);
if (task.thumbnail == null) {
- if (isForemostTask) {
- // We force loading the thumbnail icon for the foremost task
- task.thumbnail = ssp.getTaskThumbnail(task.key.id);
- if (task.thumbnail != null) {
- task.thumbnail.setHasAlpha(false);
- } else {
- task.thumbnail = mDefaultThumbnail;
- }
- // Even if we can't load the thumbnail we set the default thumbnail into
- // the cache. This will remain until the task's last active time is updated.
+ // Load the thumbnail from the system
+ task.thumbnail = ssp.getTaskThumbnail(task.key.id);
+ if (task.thumbnail != null) {
+ task.thumbnail.setHasAlpha(false);
mThumbnailCache.put(task.key, task.thumbnail);
- } else {
- // Either the task has changed since the last active time, or it was not
- // previously cached, so try and load the task anew.
- tasksToLoad.add(task);
}
}
+ if (task.thumbnail == null) {
+ // Either the task has changed since the last active time, or it was not
+ // previously cached, so try and load the task anew.
+ tasksToLoad.add(task);
+ }
}
// Add the task to the stack
@@ -380,13 +383,9 @@
// Simulate the groupings that we describe
stack.createAffiliatedGroupings();
- // Start the task loader
+ // Start the task loader and add all the tasks we need to load
mLoader.start(context);
-
- // Add all the tasks that we are reloading
- for (Task t : tasksToLoad) {
- mLoadQueue.addTask(t);
- }
+ mLoadQueue.addTasks(tasksToLoad);
// Update the package monitor with the list of packages to listen for
mPackageMonitor.setTasks(tasks);
@@ -414,18 +413,14 @@
/** Acquires the task resource data directly from the pool. */
public void loadTaskData(Task t) {
- Drawable applicationIcon = mApplicationIconCache.get(t.key);
- Bitmap thumbnail = mThumbnailCache.get(t.key);
+ Drawable applicationIcon = mApplicationIconCache.getAndInvalidateIfModified(t.key);
+ Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(t.key);
- boolean requiresLoad = false;
- if (applicationIcon == null) {
- applicationIcon = mDefaultApplicationIcon;
- requiresLoad = true;
- }
- if (thumbnail == null) {
- thumbnail = mLoadingThumbnail;
- requiresLoad = true;
- }
+ // Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
+ // use the default assets in their place until they load
+ boolean requiresLoad = (applicationIcon == null) || (thumbnail == null);
+ applicationIcon = applicationIcon != null ? applicationIcon : mDefaultApplicationIcon;
+ thumbnail = thumbnail != null ? thumbnail : mDefaultThumbnail;
if (requiresLoad) {
mLoadQueue.addTask(t);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 1670735..2473352 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -51,10 +51,6 @@
this.lastActiveTime = lastActiveTime;
}
- public void updateLastActiveTime(long lastActiveTime) {
- this.lastActiveTime = lastActiveTime;
- }
-
@Override
public boolean equals(Object o) {
if (!(o instanceof TaskKey)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 78a99e0..e1c652e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -38,6 +38,7 @@
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsPackageMonitor;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.SpaceNode;
@@ -458,7 +459,7 @@
Intent i = new Intent(task.key.baseIntent);
i.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
| Intent.FLAG_ACTIVITY_TASK_ON_HOME);
- if ((i.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) == 0) {
+ if (!Utilities.isDocument(i)) {
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
try {
@@ -510,11 +511,8 @@
loader.deleteTaskData(t, false);
// Remove the old task from activity manager
- int flags = t.key.baseIntent.getFlags();
- boolean isDocument = (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ==
- Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(t.key.id,
- isDocument);
+ Utilities.isDocument(t.key.baseIntent));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 4640067..7cc8ed5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -30,8 +30,10 @@
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
+import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManagerGlobal;
@@ -64,15 +66,37 @@
filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiver(mReceiver, filter);
- refreshUsers();
+ filter.addAction(Intent.ACTION_USER_STOPPING);
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.OWNER, filter,
+ null /* permission */, null /* scheduler */);
+ refreshUsers(UserHandle.USER_NULL);
}
- private void refreshUsers() {
- new AsyncTask<Void, Void, ArrayList<UserRecord>>() {
+ /**
+ * Refreshes users from UserManager.
+ *
+ * The pictures are only loaded if they have not been loaded yet.
+ *
+ * @param forcePictureLoadForId forces the picture of the given user to be reloaded.
+ */
+ private void refreshUsers(int forcePictureLoadForId) {
+ SparseArray<Bitmap> bitmaps = new SparseArray<>(mUsers.size());
+ final int N = mUsers.size();
+ for (int i = 0; i < N; i++) {
+ UserRecord r = mUsers.get(i);
+ if (r == null || r.info == null
+ || r.info.id == forcePictureLoadForId || r.picture == null) {
+ continue;
+ }
+ bitmaps.put(r.info.id, r.picture);
+ }
+
+ new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() {
+ @SuppressWarnings("unchecked")
@Override
- protected ArrayList<UserRecord> doInBackground(Void... params) {
+ protected ArrayList<UserRecord> doInBackground(SparseArray<Bitmap>... params) {
+ final SparseArray<Bitmap> bitmaps = params[0];
List<UserInfo> infos = mUserManager.getUsers(true);
if (infos == null) {
return null;
@@ -87,8 +111,11 @@
guestRecord = new UserRecord(info, null /* picture */,
true /* isGuest */, isCurrent);
} else if (!info.isManagedProfile()) {
- records.add(new UserRecord(info, mUserManager.getUserIcon(info.id),
- false /* isGuest */, isCurrent));
+ Bitmap picture = bitmaps.get(info.id);
+ if (picture == null) {
+ picture = mUserManager.getUserIcon(info.id);
+ }
+ records.add(new UserRecord(info, picture, false /* isGuest */, isCurrent));
}
}
@@ -109,7 +136,7 @@
notifyAdapters();
}
}
- }.execute((Void[])null);
+ }.execute((SparseArray)bitmaps);
}
private void notifyAdapters() {
@@ -134,9 +161,16 @@
}
if (ActivityManager.getCurrentUser() == id) {
+ if (record.isGuest) {
+ exitGuest(id);
+ }
return;
}
+ switchToUserId(id);
+ }
+
+ private void switchToUserId(int id) {
try {
WindowManagerGlobal.getWindowManagerService().lockNow(null);
ActivityManagerNative.getDefault().switchUser(id);
@@ -145,6 +179,12 @@
}
}
+ private void exitGuest(int id) {
+ // TODO: show confirmation dialog
+ switchToUserId(UserHandle.USER_OWNER);
+ mUserManager.removeUser(id);
+ }
+
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -153,15 +193,20 @@
final int N = mUsers.size();
for (int i = 0; i < N; i++) {
UserRecord record = mUsers.get(i);
+ if (record.info == null) continue;
boolean shouldBeCurrent = record.info.id == currentId;
if (record.isCurrent != shouldBeCurrent) {
mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent));
}
}
notifyAdapters();
- } else {
- refreshUsers();
}
+ int forcePictureLoadForId = UserHandle.USER_NULL;
+ if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) {
+ forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+ UserHandle.USER_NULL);
+ }
+ refreshUsers(forcePictureLoadForId);
}
};
@@ -195,12 +240,25 @@
@Override
public long getItemId(int position) {
- return mController.mUsers.get(position).info.id;
+ return position;
}
public void switchTo(UserRecord record) {
mController.switchTo(record);
}
+
+ public String getName(Context context, UserRecord item) {
+ if (item.isGuest) {
+ if (item.isCurrent) {
+ return context.getString(R.string.guest_exit_guest);
+ } else {
+ return context.getString(
+ item.info == null ? R.string.guest_new_guest : R.string.guest_nickname);
+ }
+ } else {
+ return item.info.name;
+ }
+ }
}
public static final class UserRecord {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index abbaacc..da0bc30 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -1729,6 +1729,7 @@
return true;
}
}
+ return false;
}
case KeyEvent.KEYCODE_MENU: {
@@ -1822,6 +1823,7 @@
return true;
}
}
+ return false;
}
case KeyEvent.KEYCODE_MENU: {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index e8e2813..f54f798 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -233,6 +233,7 @@
static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
+ static final long TIMEOUT_RESTORE_FINISHED_INTERVAL = 30 * 1000;
// User confirmation timeout for a full backup/restore operation. It's this long in
// order to give them time to enter the backup password.
@@ -6309,6 +6310,7 @@
RUNNING_QUEUE,
RESTORE_KEYVALUE,
RESTORE_FULL,
+ RESTORE_FINISHED,
FINAL
}
@@ -6343,6 +6345,9 @@
// Our bookkeeping about the ancestral dataset
private PackageManagerBackupAgent mPmAgent;
+ // Currently-bound backup agent for restore + restoreFinished purposes
+ private IBackupAgent mAgent;
+
// What sort of restore we're doing now
private RestoreDescription mRestoreDescription;
@@ -6441,6 +6446,13 @@
// this one is always valid too
}
}
+
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size());
+ for (PackageInfo info : mAcceptSet) {
+ Slog.v(TAG, " " + info.packageName);
+ }
+ }
}
private String[] packagesToNames(List<PackageInfo> apps) {
@@ -6473,6 +6485,10 @@
restoreFull();
break;
+ case RESTORE_FINISHED:
+ restoreFinished();
+ break;
+
case FINAL:
if (!mFinished) finalizeRestore();
else {
@@ -6529,7 +6545,7 @@
private void startRestore() {
sendStartRestore(mAcceptSet.size());
- UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE;
+ UnifiedRestoreState nextState = UnifiedRestoreState.RESTORE_FINISHED;
try {
// If we don't yet have PM metadata for this token, synthesize an
// entry for the PM pseudopackage and make it the first to be
@@ -6544,6 +6560,7 @@
mPmAgent = new PackageManagerBackupAgent(metadataFile);
} catch (IOException e) {
// Nope, we need to get it via restore
+ if (MORE_DEBUG) Slog.v(TAG, "Need to restore @pm@");
PackageInfo pmPackage = new PackageInfo();
pmPackage.packageName = PACKAGE_MANAGER_SENTINEL;
mAcceptSet.add(0, pmPackage);
@@ -6581,8 +6598,11 @@
mCurrentPackage = new PackageInfo();
mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
- initiateOneRestore(mCurrentPackage, 0,
- IBackupAgent.Stub.asInterface(mPmAgent.onBind()));
+ mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "initiating restore for PMBA");
+ }
+ initiateOneRestore(mCurrentPackage, 0);
// The PM agent called operationComplete() already, because our invocation
// of it is process-local and therefore synchronous. That means that a
// RUNNING_QUEUE message is already enqueued. Only if we're unable to
@@ -6621,6 +6641,11 @@
nextState = UnifiedRestoreState.FINAL;
return;
}
+ } else {
+ // We have the PMBA already, so we can proceed directly to
+ // the RUNNING_QUEUE state ourselves.
+ if (MORE_DEBUG) Slog.v(TAG, "PMBA from cache; proceeding to run queue");
+ nextState = UnifiedRestoreState.RUNNING_QUEUE;
}
} catch (RemoteException e) {
// If we lost the transport at any time, halt
@@ -6762,10 +6787,10 @@
}
// Good to go! Set up and bind the agent...
- IBackupAgent agent = bindToAgentSynchronous(
+ mAgent = bindToAgentSynchronous(
mCurrentPackage.applicationInfo,
IApplicationThread.BACKUP_MODE_INCREMENTAL);
- if (agent == null) {
+ if (mAgent == null) {
Slog.w(TAG, "Can't find backup agent for " + packageName);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
"Restore agent missing");
@@ -6775,7 +6800,7 @@
// And then finally start the restore on this agent
try {
- initiateOneRestore(mCurrentPackage, metaInfo.versionCode, agent);
+ initiateOneRestore(mCurrentPackage, metaInfo.versionCode);
++mCount;
} catch (Exception e) {
Slog.e(TAG, "Error when attempting restore: " + e.toString());
@@ -6785,7 +6810,7 @@
}
// Guts of a key/value restore operation
- void initiateOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) {
+ void initiateOneRestore(PackageInfo app, int appVersionCode) {
final String packageName = app.packageName;
if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
@@ -6881,7 +6906,7 @@
// the operationComplete() callback will schedule the next step,
// so we do not do that here.
prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
- agent.doRestore(mBackupData, appVersionCode, mNewState,
+ mAgent.doRestore(mBackupData, appVersionCode, mNewState,
token, mBackupManagerBinder);
} catch (Exception e) {
Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
@@ -6926,6 +6951,20 @@
}
}
+ // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end
+ private void restoreFinished() {
+ try {
+ final int token = generateToken();
+ prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this);
+ mAgent.doRestoreFinished(token, mBackupManagerBinder);
+ // If we get this far, the callback or timeout will schedule the
+ // next restore state, so we're done
+ } catch (Exception e) {
+ Slog.e(TAG, "Unable to finalize restore of " + mCurrentPackage.packageName);
+ executeNextState(UnifiedRestoreState.FINAL);
+ }
+ }
+
class StreamFeederThread extends RestoreEngine implements Runnable {
final String TAG = "StreamFeederThread";
FullRestoreEngine mEngine;
@@ -7202,23 +7241,58 @@
@Override
public void operationComplete() {
- int size = (int) mBackupDataName.length();
- EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, mCurrentPackage.packageName, size);
-
- // Just go back to running the restore queue
- keyValueAgentCleanup();
-
- // If there was widget state associated with this app, get the OS to
- // incorporate it into current bookeeping and then pass that along to
- // the app as part of the restore operation.
- if (mWidgetData != null) {
- restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "operationComplete() during restore: target="
+ + mCurrentPackage.packageName
+ + " state=" + mState);
}
- executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
+ final UnifiedRestoreState nextState;
+ switch (mState) {
+ case RESTORE_KEYVALUE:
+ case RESTORE_FULL: {
+ // Okay, we've just heard back from the agent that it's done with
+ // the restore itself. We now have to send the same agent its
+ // doRestoreFinished() callback, so roll into that state.
+ nextState = UnifiedRestoreState.RESTORE_FINISHED;
+ break;
+ }
+
+ case RESTORE_FINISHED: {
+ // Okay, we're done with this package. Tidy up and go on to the next
+ // app in the queue.
+ int size = (int) mBackupDataName.length();
+ EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE,
+ mCurrentPackage.packageName, size);
+
+ // Just go back to running the restore queue
+ keyValueAgentCleanup();
+
+ // If there was widget state associated with this app, get the OS to
+ // incorporate it into current bookeeping and then pass that along to
+ // the app as part of the restore-time work.
+ if (mWidgetData != null) {
+ restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
+ }
+
+ nextState = UnifiedRestoreState.RUNNING_QUEUE;
+ break;
+ }
+
+ default: {
+ // Some kind of horrible semantic error; we're in an unexpected state.
+ // Back off hard and wind up.
+ Slog.e(TAG, "Unexpected restore callback into state " + mState);
+ keyValueAgentErrorCleanup();
+ nextState = UnifiedRestoreState.FINAL;
+ break;
+ }
+ }
+
+ executeNextState(nextState);
}
- // A call to agent.doRestore() has timed out
+ // A call to agent.doRestore() or agent.doRestoreFinished() has timed out
@Override
public void handleTimeout() {
Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8b7e0d6..31c1c6c 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -364,28 +364,6 @@
// devices.
private boolean mShowDialogs = true;
- /**
- * Description of a request to start a new activity, which has been held
- * due to app switches being disabled.
- */
- static class PendingActivityLaunch {
- final ActivityRecord r;
- final ActivityRecord sourceRecord;
- final int startFlags;
- final ActivityStack stack;
-
- PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
- int _startFlags, ActivityStack _stack) {
- r = _r;
- sourceRecord = _sourceRecord;
- startFlags = _startFlags;
- stack = _stack;
- }
- }
-
- final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
- = new ArrayList<PendingActivityLaunch>();
-
BroadcastQueue mFgBroadcastQueue;
BroadcastQueue mBgBroadcastQueue;
// Convenient for easy iteration over the queues. Foreground is first
@@ -1438,7 +1416,7 @@
} break;
case DO_PENDING_ACTIVITY_LAUNCHES_MSG: {
synchronized (ActivityManagerService.this) {
- doPendingActivityLaunchesLocked(true);
+ mStackSupervisor.doPendingActivityLaunchesLocked(true);
}
} break;
case KILL_APPLICATION_MSG: {
@@ -3339,19 +3317,6 @@
mProcessObservers.finishBroadcast();
}
- final void doPendingActivityLaunchesLocked(boolean doResume) {
- final int N = mPendingActivityLaunches.size();
- if (N <= 0) {
- return;
- }
- for (int i=0; i<N; i++) {
- PendingActivityLaunch pal = mPendingActivityLaunches.get(i);
- mStackSupervisor.startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
- doResume && i == (N-1), null);
- }
- mPendingActivityLaunches.clear();
- }
-
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
@@ -9140,6 +9105,7 @@
}
}
+ @Override
public void stopAppSwitches() {
if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES)
!= PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 32f2624..91bc7e3 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2732,18 +2732,23 @@
return r;
}
- void finishAllActivitiesLocked() {
+ void finishAllActivitiesLocked(boolean immediately) {
+ boolean noActivitiesInStack = true;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
- if (r.finishing) {
+ noActivitiesInStack = false;
+ if (r.finishing && !immediately) {
continue;
}
- Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r);
+ Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r + " immediately");
finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
}
}
+ if (noActivitiesInStack) {
+ mActivityContainer.onTaskListEmptyLocked();
+ }
}
final boolean navigateUpToLocked(IBinder token, Intent destIntent, int resultCode,
@@ -2858,6 +2863,7 @@
// down to the max limit while they are still waiting to finish.
mStackSupervisor.mFinishingActivities.remove(r);
mStackSupervisor.mWaitingVisibleActivities.remove(r);
+ mStackSupervisor.removePendingActivityLaunchesLocked(r);
// Remove any pending results.
if (r.finishing && r.pendingResults != null) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 0310962..4cfd042 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -98,7 +98,6 @@
import com.android.internal.os.TransferPipe;
import com.android.internal.statusbar.IStatusBarService;
import com.android.server.LocalServices;
-import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.wm.WindowManagerService;
@@ -271,6 +270,28 @@
*/
private LockTaskNotify mLockTaskNotify;
+ final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
+ = new ArrayList<PendingActivityLaunch>();
+
+ /**
+ * Description of a request to start a new activity, which has been held
+ * due to app switches being disabled.
+ */
+ static class PendingActivityLaunch {
+ final ActivityRecord r;
+ final ActivityRecord sourceRecord;
+ final int startFlags;
+ final ActivityStack stack;
+
+ PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
+ int _startFlags, ActivityStack _stack) {
+ r = _r;
+ sourceRecord = _sourceRecord;
+ startFlags = _startFlags;
+ stack = _stack;
+ }
+ }
+
public ActivityStackSupervisor(ActivityManagerService service) {
mService = service;
mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
@@ -1421,7 +1442,7 @@
if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
PendingActivityLaunch pal =
new PendingActivityLaunch(r, sourceRecord, startFlags, stack);
- mService.mPendingActivityLaunches.add(pal);
+ mPendingActivityLaunches.add(pal);
setDismissKeyguard(false);
ActivityOptions.abort(options);
return ActivityManager.START_SWITCHES_CANCELED;
@@ -1439,7 +1460,7 @@
mService.mDidAppSwitch = true;
}
- mService.doPendingActivityLaunchesLocked(false);
+ doPendingActivityLaunchesLocked(false);
err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, true, options);
@@ -2008,6 +2029,23 @@
return ActivityManager.START_SUCCESS;
}
+ final void doPendingActivityLaunchesLocked(boolean doResume) {
+ while (!mPendingActivityLaunches.isEmpty()) {
+ PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
+ startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
+ doResume && mPendingActivityLaunches.isEmpty(), null);
+ }
+ }
+
+ void removePendingActivityLaunchesLocked(ActivityRecord r) {
+ for (int palNdx = mPendingActivityLaunches.size() - 1; palNdx >= 0; --palNdx) {
+ PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx);
+ if (pal.r == r) {
+ mPendingActivityLaunches.remove(palNdx);
+ }
+ }
+ }
+
void acquireLaunchWakelock() {
if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
throw new IllegalStateException("Calling must be system uid");
@@ -3312,7 +3350,9 @@
synchronized (mService) {
Slog.w(TAG, "Timeout waiting for all activities in task to finish. " +
msg.obj);
- ((ActivityContainer) msg.obj).onTaskListEmptyLocked();
+ final ActivityContainer container = (ActivityContainer) msg.obj;
+ container.mStack.finishAllActivitiesLocked(true);
+ container.onTaskListEmptyLocked();
}
} break;
case LAUNCH_TASK_BEHIND_COMPLETE: {
@@ -3414,11 +3454,11 @@
final Message msg =
mHandler.obtainMessage(CONTAINER_TASK_LIST_EMPTY_TIMEOUT, this);
- mHandler.sendMessageDelayed(msg, 1000);
+ mHandler.sendMessageDelayed(msg, 2000);
long origId = Binder.clearCallingIdentity();
try {
- mStack.finishAllActivitiesLocked();
+ mStack.finishAllActivitiesLocked(false);
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 727cff0..a70dd1b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6231,31 +6231,26 @@
info.nativeLibraryDir = null;
info.secondaryNativeLibraryDir = null;
- if (bundledApp) {
- // Monolithic bundled install
- // TODO: support cluster bundled installs?
-
- final boolean is64Bit = (info.primaryCpuAbi != null)
- && VMRuntime.is64BitAbi(info.primaryCpuAbi);
-
- // This is a bundled system app so choose the path based on the ABI.
- // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this
- // is just the default path.
- final String apkName = deriveCodePathName(codePath);
- final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;
- info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir,
- apkName).getAbsolutePath();
- info.nativeLibraryRootRequiresIsa = false;
-
- info.nativeLibraryDir = info.nativeLibraryRootDir;
- if (info.secondaryCpuAbi != null) {
- final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME;
- info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
- secondaryLibDir, apkName).getAbsolutePath();
- }
- } else if (isApkFile(codeFile)) {
+ if (isApkFile(codeFile)) {
// Monolithic install
- if (asecApp) {
+ if (bundledApp) {
+ final boolean is64Bit = VMRuntime.is64BitInstructionSet(
+ getPrimaryInstructionSet(info));
+
+ // This is a bundled system app so choose the path based on the ABI.
+ // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this
+ // is just the default path.
+ final String apkName = deriveCodePathName(codePath);
+ final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;
+ info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir,
+ apkName).getAbsolutePath();
+
+ if (info.secondaryCpuAbi != null) {
+ final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME;
+ info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
+ secondaryLibDir, apkName).getAbsolutePath();
+ }
+ } else if (asecApp) {
info.nativeLibraryRootDir = new File(codeFile.getParentFile(), LIB_DIR_NAME)
.getAbsolutePath();
} else {
@@ -6271,10 +6266,8 @@
info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
info.nativeLibraryRootRequiresIsa = true;
- if (info.primaryCpuAbi != null) {
- info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
- VMRuntime.getInstructionSet(info.primaryCpuAbi)).getAbsolutePath();
- }
+ info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
+ getPrimaryInstructionSet(info)).getAbsolutePath();
if (info.secondaryCpuAbi != null) {
info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 43469ba..b4cc252 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -843,7 +843,8 @@
writeBoolean(serializer, restrictions, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_TELEPHONY);
+ writeBoolean(serializer, restrictions, UserManager.DISALLOW_OUTGOING_CALLS);
+ writeBoolean(serializer, restrictions, UserManager.DISALLOW_SMS);
writeBoolean(serializer, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
serializer.endTag(null, TAG_RESTRICTIONS);
}
@@ -986,7 +987,8 @@
UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
readBoolean(parser, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
readBoolean(parser, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
- readBoolean(parser, restrictions, UserManager.DISALLOW_TELEPHONY);
+ readBoolean(parser, restrictions, UserManager.DISALLOW_OUTGOING_CALLS);
+ readBoolean(parser, restrictions, UserManager.DISALLOW_SMS);
readBoolean(parser, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index 4a8623d..bed85fc 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -94,7 +94,7 @@
}
public boolean addOrUpdateKeyphraseSoundModel(KeyphraseSoundModel soundModel) {
- SQLiteDatabase db = this.getWritableDatabase();
+ SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
// Generate a random ID for the model.
values.put(SoundModelContract.KEY_ID, soundModel.uuid.toString());
@@ -105,7 +105,7 @@
if (db.insertWithOnConflict(
SoundModelContract.TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE) != -1) {
for (Keyphrase keyphrase : soundModel.keyphrases) {
- status &= addKeyphrase(soundModel.uuid, keyphrase);
+ status &= addOrUpdateKeyphrase(soundModel.uuid, keyphrase);
}
return status;
} else {
@@ -114,18 +114,16 @@
}
}
- /**
- * TODO(sansid): Change to addOrUpdate to handle changes here.
- */
- private boolean addKeyphrase(UUID modelId, Keyphrase keyphrase) {
- SQLiteDatabase db = this.getWritableDatabase();
+ private boolean addOrUpdateKeyphrase(UUID modelId, Keyphrase keyphrase) {
+ SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KeyphraseContract.KEY_ID, keyphrase.id);
values.put(KeyphraseContract.KEY_RECOGNITION_MODES, keyphrase.recognitionModeFlags);
- values.put(KeyphraseContract.KEY_SOUND_MODEL_ID, keyphrase.id);
+ values.put(KeyphraseContract.KEY_SOUND_MODEL_ID, modelId.toString());
values.put(KeyphraseContract.KEY_HINT_TEXT, keyphrase.hintText);
values.put(KeyphraseContract.KEY_LOCALE, keyphrase.locale);
- if (db.insert(KeyphraseContract.TABLE, null, values) != -1) {
+ if (db.insertWithOnConflict(
+ KeyphraseContract.TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE) != -1) {
return true;
} else {
Slog.w(TAG, "Failed to persist keyphrase to database");
@@ -134,6 +132,26 @@
}
/**
+ * Deletes the sound model and associated keyphrases.
+ */
+ public boolean deleteKeyphraseSoundModel(UUID uuid) {
+ SQLiteDatabase db = getWritableDatabase();
+ String modelId = uuid.toString();
+ String soundModelClause = SoundModelContract.KEY_ID + "=" + modelId;
+ boolean status = true;
+ if (db.delete(SoundModelContract.TABLE, soundModelClause, null) == 0) {
+ Slog.w(TAG, "No sound models deleted from the database");
+ status = false;
+ }
+ String keyphraseClause = KeyphraseContract.KEY_SOUND_MODEL_ID + "=" + modelId;
+ if (db.delete(KeyphraseContract.TABLE, keyphraseClause, null) == 0) {
+ Slog.w(TAG, "No keyphrases deleted from the database");
+ status = false;
+ }
+ return status;
+ }
+
+ /**
* Lists all the keyphrase sound models currently registered with the system.
*/
public List<KeyphraseSoundModel> getKephraseSoundModels() {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 7204695..63702ba 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -281,12 +281,25 @@
throw new SecurityException("Caller does not hold the permission "
+ Manifest.permission.MANAGE_VOICE_KEYPHRASES);
}
+ if (model == null) {
+ throw new IllegalArgumentException("Model must not be null");
+ }
+
final long caller = Binder.clearCallingIdentity();
try {
- if (mDbHelper.addOrUpdateKeyphraseSoundModel(model)) {
- return STATUS_OK;
+ // If the keyphrases are not present in the model, delete the model.
+ if (model.keyphrases == null) {
+ if (mDbHelper.deleteKeyphraseSoundModel(model.uuid)) {
+ return STATUS_OK;
+ } else {
+ return STATUS_ERROR;
+ }
} else {
- return STATUS_ERROR;
+ if (mDbHelper.addOrUpdateKeyphraseSoundModel(model)) {
+ return STATUS_OK;
+ } else {
+ return STATUS_ERROR;
+ }
}
} finally {
Binder.restoreCallingIdentity(caller);