diff options
Diffstat (limited to 'core/java')
24 files changed, 702 insertions, 129 deletions
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 5262a5f..2a17fa6 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -25,4 +25,6 @@ public abstract class ActivityManagerInternal { // Called by the power manager. public abstract void goingToSleep(); public abstract void wakingUp(); + public abstract int startIsolatedProcess(String entryPoint, String[] mainArgs, + String processName, String abiOverride, int uid, Runnable crashHandler); } diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index 1326064..b5d362d 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -256,7 +256,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { @Override protected Transition getViewsTransition() { if (mIsReturning) { - return getWindow().getExitTransition(); + return getWindow().getReenterTransition(); } else { return getWindow().getEnterTransition(); } @@ -264,7 +264,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { protected Transition getSharedElementTransition() { if (mIsReturning) { - return getWindow().getSharedElementExitTransition(); + return getWindow().getSharedElementReenterTransition(); } else { return getWindow().getSharedElementEnterTransition(); } diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java index 2ce6018..b3fdcc7 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -395,7 +395,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { @Override protected Transition getViewsTransition() { if (mIsReturning) { - return getWindow().getEnterTransition(); + return getWindow().getReturnTransition(); } else { return getWindow().getExitTransition(); } @@ -403,7 +403,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { protected Transition getSharedElementTransition() { if (mIsReturning) { - return getWindow().getSharedElementEnterTransition(); + return getWindow().getSharedElementReturnTransition(); } else { return getWindow().getSharedElementExitTransition(); } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 5347f03..772e132 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -454,7 +454,7 @@ public interface IActivityManager extends IInterface { * Private non-Binder interfaces */ /* package */ boolean testIsSystemReady(); - + /** Information you can retrieve about a particular application. */ public static class ContentProviderHolder implements Parcelable { public final ProviderInfo info; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index c7fdbed..acf7ade 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -247,7 +247,6 @@ public class Notification implements Parcelable /** - * @hide * A medium-format version of {@link #contentView}, providing the Notification an * opportunity to add action buttons to contentView. At its discretion, the system UI may * choose to show this as a heads-up notification, which will pop up so the user can see diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 8be52a2..e28f00c 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -449,9 +449,17 @@ public class DevicePolicyManager { * active (enabled) in the system. */ public boolean isAdminActive(ComponentName who) { + return isAdminActiveAsUser(who, UserHandle.myUserId()); + } + + /** + * @see #isAdminActive(ComponentName) + * @hide + */ + public boolean isAdminActiveAsUser(ComponentName who, int userId) { if (mService != null) { try { - return mService.isAdminActive(who, UserHandle.myUserId()); + return mService.isAdminActive(who, userId); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -465,9 +473,17 @@ public class DevicePolicyManager { * returned. */ public List<ComponentName> getActiveAdmins() { + return getActiveAdminsAsUser(UserHandle.myUserId()); + } + + /** + * @see #getActiveAdmins() + * @hide + */ + public List<ComponentName> getActiveAdminsAsUser(int userId) { if (mService != null) { try { - return mService.getActiveAdmins(UserHandle.myUserId()); + return mService.getActiveAdmins(userId); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -2314,9 +2330,17 @@ public class DevicePolicyManager { * @throws IllegalArgumentException if the userId is invalid. */ public ComponentName getProfileOwner() throws IllegalArgumentException { + return getProfileOwnerAsUser(Process.myUserHandle().getIdentifier()); + } + + /** + * @see #getProfileOwner() + * @hide + */ + public ComponentName getProfileOwnerAsUser(final int userId) throws IllegalArgumentException { if (mService != null) { try { - return mService.getProfileOwner(Process.myUserHandle().getIdentifier()); + return mService.getProfileOwner(userId); } catch (RemoteException re) { Log.w(TAG, "Failed to get profile owner"); throw new IllegalArgumentException( @@ -2856,7 +2880,7 @@ public class DevicePolicyManager { * @see #setAccountManagementDisabled */ public String[] getAccountTypesWithManagementDisabled() { - return getAccountTypesWithManagementDisabledAsUser(UserHandle.getCallingUserId()); + return getAccountTypesWithManagementDisabledAsUser(UserHandle.myUserId()); } /** diff --git a/core/java/android/content/pm/IPackageInstallerCallback.aidl b/core/java/android/content/pm/IPackageInstallerCallback.aidl index a31ae54..39ae1a0 100644 --- a/core/java/android/content/pm/IPackageInstallerCallback.aidl +++ b/core/java/android/content/pm/IPackageInstallerCallback.aidl @@ -19,6 +19,8 @@ package android.content.pm; /** {@hide} */ oneway interface IPackageInstallerCallback { void onSessionCreated(int sessionId); + void onSessionOpened(int sessionId); void onSessionProgressChanged(int sessionId, float progress); + void onSessionClosed(int sessionId); void onSessionFinished(int sessionId, boolean success); } diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl index 2fd7ddb..af0323f 100644 --- a/core/java/android/content/pm/IPackageInstallerSession.aidl +++ b/core/java/android/content/pm/IPackageInstallerSession.aidl @@ -24,7 +24,9 @@ interface IPackageInstallerSession { void setClientProgress(float progress); void addClientProgress(float progress); + String[] list(); ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes); + ParcelFileDescriptor openRead(String name); void close(); void commit(in IPackageInstallObserver2 observer); diff --git a/core/java/android/content/pm/InstallSessionInfo.java b/core/java/android/content/pm/InstallSessionInfo.java index a9c574a..f263885 100644 --- a/core/java/android/content/pm/InstallSessionInfo.java +++ b/core/java/android/content/pm/InstallSessionInfo.java @@ -16,7 +16,9 @@ package android.content.pm; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Intent; import android.graphics.Bitmap; import android.os.Parcel; import android.os.Parcelable; @@ -32,6 +34,8 @@ public class InstallSessionInfo implements Parcelable { public String installerPackageName; /** {@hide} */ public float progress; + /** {@hide} */ + public boolean open; /** {@hide} */ public int mode; @@ -53,6 +57,7 @@ public class InstallSessionInfo implements Parcelable { sessionId = source.readInt(); installerPackageName = source.readString(); progress = source.readFloat(); + open = source.readInt() != 0; mode = source.readInt(); sizeBytes = source.readLong(); @@ -88,6 +93,13 @@ public class InstallSessionInfo implements Parcelable { } /** + * Return if this session is currently open. + */ + public boolean isOpen() { + return open; + } + + /** * Return the package name this session is working with. May be {@code null} * if unknown. */ @@ -111,6 +123,23 @@ public class InstallSessionInfo implements Parcelable { return appLabel; } + /** + * Return an Intent that can be started to view details about this install + * session. This may surface actions such as pause, resume, or cancel. + * <p> + * In some cases, a matching Activity may not exist, so ensure you safeguard + * against this. + * + * @see PackageInstaller#ACTION_SESSION_DETAILS + */ + public @Nullable Intent getDetailsIntent() { + final Intent intent = new Intent(PackageInstaller.ACTION_SESSION_DETAILS); + intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); + intent.setPackage(installerPackageName); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return intent; + } + @Override public int describeContents() { return 0; @@ -121,6 +150,7 @@ public class InstallSessionInfo implements Parcelable { dest.writeInt(sessionId); dest.writeString(installerPackageName); dest.writeFloat(progress); + dest.writeInt(open ? 1 : 0); dest.writeInt(mode); dest.writeLong(sizeBytes); diff --git a/core/java/android/content/pm/InstallSessionParams.java b/core/java/android/content/pm/InstallSessionParams.java index 3de9863..1716e39 100644 --- a/core/java/android/content/pm/InstallSessionParams.java +++ b/core/java/android/content/pm/InstallSessionParams.java @@ -17,6 +17,7 @@ package android.content.pm; import android.annotation.Nullable; +import android.app.ActivityManager; import android.content.Intent; import android.graphics.Bitmap; import android.net.Uri; @@ -30,6 +31,9 @@ import com.android.internal.util.IndentingPrintWriter; */ public class InstallSessionParams implements Parcelable { + /** {@hide} */ + public static final int MODE_INVALID = -1; + /** * Mode for an install session whose staged APKs should fully replace any * existing APKs for the target app. @@ -48,21 +52,19 @@ public class InstallSessionParams implements Parcelable { public static final int MODE_INHERIT_EXISTING = 2; /** {@hide} */ - public int mode; + public int mode = MODE_INVALID; /** {@hide} */ public int installFlags; /** {@hide} */ public int installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY; /** {@hide} */ - public Signature[] signatures; - /** {@hide} */ public long sizeBytes = -1; /** {@hide} */ public String appPackageName; /** {@hide} */ public Bitmap appIcon; /** {@hide} */ - public CharSequence appLabel; + public String appLabel; /** {@hide} */ public Uri originatingUri; /** {@hide} */ @@ -86,7 +88,6 @@ public class InstallSessionParams implements Parcelable { mode = source.readInt(); installFlags = source.readInt(); installLocation = source.readInt(); - signatures = (Signature[]) source.readParcelableArray(null); sizeBytes = source.readLong(); appPackageName = source.readString(); appIcon = source.readParcelable(null); @@ -106,16 +107,13 @@ public class InstallSessionParams implements Parcelable { } /** - * Optionally provide a set of certificates for the app being installed. - * <p> - * If the APKs staged in the session aren't consistent with these - * signatures, the install will fail. Regardless of this value, all APKs in - * the app must have the same signing certificates. - * - * @see PackageInfo#signatures + * @deprecated use {@link PackageInstaller.Session#openRead(String)} to + * calculate message digest instead. + * @hide */ + @Deprecated public void setSignatures(@Nullable Signature[] signatures) { - this.signatures = signatures; + throw new UnsupportedOperationException(); } /** @@ -146,7 +144,8 @@ public class InstallSessionParams implements Parcelable { /** * Optionally set an icon representing the app being installed. This should - * be at least {@link android.R.dimen#app_icon_size} in both dimensions. + * be roughly {@link ActivityManager#getLauncherLargeIconSize()} in both + * dimensions. */ public void setAppIcon(@Nullable Bitmap appIcon) { this.appIcon = appIcon; @@ -156,7 +155,7 @@ public class InstallSessionParams implements Parcelable { * Optionally set a label representing the app being installed. */ public void setAppLabel(@Nullable CharSequence appLabel) { - this.appLabel = appLabel; + this.appLabel = (appLabel != null) ? appLabel.toString() : null; } /** @@ -184,7 +183,6 @@ public class InstallSessionParams implements Parcelable { pw.printPair("mode", mode); pw.printHexPair("installFlags", installFlags); pw.printPair("installLocation", installLocation); - pw.printPair("signatures", (signatures != null)); pw.printPair("sizeBytes", sizeBytes); pw.printPair("appPackageName", appPackageName); pw.printPair("appIcon", (appIcon != null)); @@ -205,11 +203,10 @@ public class InstallSessionParams implements Parcelable { dest.writeInt(mode); dest.writeInt(installFlags); dest.writeInt(installLocation); - dest.writeParcelableArray(signatures, flags); dest.writeLong(sizeBytes); dest.writeString(appPackageName); dest.writeParcelable(appIcon, flags); - dest.writeString(appLabel != null ? appLabel.toString() : null); + dest.writeString(appLabel); dest.writeParcelable(originatingUri, flags); dest.writeParcelable(referrerUri, flags); dest.writeString(abiOverride); diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index a114bb8..8af827e 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -18,9 +18,10 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.app.PackageInstallObserver; import android.app.PackageUninstallObserver; -import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; import android.os.FileBridge; import android.os.Handler; @@ -32,7 +33,9 @@ import android.util.ExceptionUtils; import java.io.Closeable; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; +import java.security.MessageDigest; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -63,6 +66,27 @@ import java.util.List; * </ul> */ public class PackageInstaller { + /** + * Activity Action: Show details about a particular install session. This + * may surface actions such as pause, resume, or cancel. + * <p> + * This should always be scoped to the installer package that owns the + * session. Clients should use {@link InstallSessionInfo#getDetailsIntent()} + * to build this intent correctly. + * <p> + * In some cases, a matching Activity may not exist, so ensure you safeguard + * against this. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS"; + + /** + * An integer session ID. + * + * @see #ACTION_SESSION_DETAILS + */ + public static final String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID"; + private final PackageManager mPm; private final IPackageInstaller mInstaller; private final int mUserId; @@ -180,14 +204,32 @@ public class PackageInstaller { /** * Events for observing session lifecycle. + * <p> + * A typical session lifecycle looks like this: + * <ul> + * <li>An installer creates a session to indicate pending app delivery. All + * install details are available at this point. + * <li>The installer opens the session to deliver APK data. Note that a + * session may be opened and closed multiple times as network connectivity + * changes. The installer may deliver periodic progress updates. + * <li>The installer commits or abandons the session, resulting in the + * session being finished. + * </ul> */ public static abstract class SessionCallback { /** - * New session has been created. + * New session has been created. Details about the session can be + * obtained from {@link PackageInstaller#getSessionInfo(int)}. */ public abstract void onCreated(int sessionId); /** + * Session has been opened. A session is usually opened when the + * installer is actively writing data. + */ + public abstract void onOpened(int sessionId); + + /** * Progress for given session has been updated. * <p> * Note that this progress may not directly correspond to the value @@ -198,6 +240,11 @@ public class PackageInstaller { public abstract void onProgressChanged(int sessionId, float progress); /** + * Session has been closed. + */ + public abstract void onClosed(int sessionId); + + /** * Session has completely finished, either with success or failure. */ public abstract void onFinished(int sessionId, boolean success); @@ -207,8 +254,10 @@ public class PackageInstaller { private static class SessionCallbackDelegate extends IPackageInstallerCallback.Stub implements Handler.Callback { private static final int MSG_SESSION_CREATED = 1; - private static final int MSG_SESSION_PROGRESS_CHANGED = 2; - private static final int MSG_SESSION_FINISHED = 3; + private static final int MSG_SESSION_OPENED = 2; + private static final int MSG_SESSION_PROGRESS_CHANGED = 3; + private static final int MSG_SESSION_CLOSED = 4; + private static final int MSG_SESSION_FINISHED = 5; final SessionCallback mCallback; final Handler mHandler; @@ -224,9 +273,15 @@ public class PackageInstaller { case MSG_SESSION_CREATED: mCallback.onCreated(msg.arg1); return true; + case MSG_SESSION_OPENED: + mCallback.onOpened(msg.arg1); + return true; case MSG_SESSION_PROGRESS_CHANGED: mCallback.onProgressChanged(msg.arg1, (float) msg.obj); return true; + case MSG_SESSION_CLOSED: + mCallback.onClosed(msg.arg1); + return true; case MSG_SESSION_FINISHED: mCallback.onFinished(msg.arg1, msg.arg2 != 0); return true; @@ -240,12 +295,22 @@ public class PackageInstaller { } @Override + public void onSessionOpened(int sessionId) { + mHandler.obtainMessage(MSG_SESSION_OPENED, sessionId, 0).sendToTarget(); + } + + @Override public void onSessionProgressChanged(int sessionId, float progress) { mHandler.obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, 0, progress) .sendToTarget(); } @Override + public void onSessionClosed(int sessionId) { + mHandler.obtainMessage(MSG_SESSION_CLOSED, sessionId, 0).sendToTarget(); + } + + @Override public void onSessionFinished(int sessionId, boolean success) { mHandler.obtainMessage(MSG_SESSION_FINISHED, sessionId, success ? 1 : 0) .sendToTarget(); @@ -373,7 +438,7 @@ public class PackageInstaller { ExceptionUtils.maybeUnwrapIOException(e); throw e; } catch (RemoteException e) { - throw new IOException(e); + throw e.rethrowAsRuntimeException(); } } @@ -391,6 +456,40 @@ public class PackageInstaller { } /** + * List all APK names contained in this session. + * <p> + * This returns all names which have been previously written through + * {@link #openWrite(String, long, long)} as part of this session. + */ + public @NonNull String[] list() { + try { + return mSession.list(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Open a stream to read an APK file from the session. + * <p> + * This is only valid for names which have been previously written + * through {@link #openWrite(String, long, long)} as part of this + * session. For example, this stream may be used to calculate a + * {@link MessageDigest} of a written APK before committing. + */ + public @NonNull InputStream openRead(@NonNull String name) throws IOException { + try { + final ParcelFileDescriptor pfd = mSession.openRead(name); + return new ParcelFileDescriptor.AutoCloseInputStream(pfd); + } catch (RuntimeException e) { + ExceptionUtils.maybeUnwrapIOException(e); + throw e; + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** * Attempt to commit everything staged in this session. This may require * user intervention, and so it may not happen immediately. The final * result of the commit will be reported through the given callback. diff --git a/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java b/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java index 27829a7..6e1844a 100644 --- a/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java +++ b/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java @@ -65,6 +65,18 @@ public final class HdmiCecDeviceInfo implements Parcelable { */ public static final int ADDR_INTERNAL = 0; + /** + * Physical address used to indicate the source comes from internal device. + * The physical address of TV(0) is used. + */ + public static final int PATH_INTERNAL = 0x0000; + + /** Invalid physical address (routing path) */ + public static final int PATH_INVALID = 0xFFFF; + + /** Invalid port ID */ + public static final int PORT_INVALID = -1; + // Logical address, physical address, device type, vendor id and display name // are immutable value. private final int mLogicalAddress; diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl index 808e0c9..920a1f4 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl @@ -37,6 +37,7 @@ import java.util.List; */ interface IHdmiControlService { int[] getSupportedTypes(); + HdmiCecDeviceInfo getActiveSource(); void oneTouchPlay(IHdmiControlCallback callback); void queryDisplayStatus(IHdmiControlCallback callback); void addHotplugEventListener(IHdmiHotplugEventListener listener); diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java index 6734878..5c7a8da 100644 --- a/core/java/android/hardware/location/GeofenceHardwareImpl.java +++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java @@ -139,8 +139,8 @@ public final class GeofenceHardwareImpl { private void updateFusedHardwareAvailability() { boolean fusedSupported; try { - fusedSupported = mFusedService.isSupported(); - } catch(RemoteException e) { + fusedSupported = (mFusedService != null ? mFusedService.isSupported() : false); + } catch (RemoteException e) { Log.e(TAG, "RemoteException calling LocationManagerService"); fusedSupported = false; } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index afac239..c3ac012 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -61,6 +61,12 @@ public class Process { public static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary"; /** + * Defines the root UID. + * @hide + */ + public static final int ROOT_UID = 0; + + /** * Defines the UID/GID under which system code runs. */ public static final int SYSTEM_UID = 1000; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index d385131..13f93a7 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -625,8 +625,9 @@ public class UserManager { Settings.Secure.putStringForUser(context.getContentResolver(), Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id); try { - mService.setUserRestrictions( - mService.getDefaultGuestRestrictions(), guest.id); + Bundle guestRestrictions = mService.getDefaultGuestRestrictions(); + guestRestrictions.putBoolean(DISALLOW_SMS, true); + mService.setUserRestrictions(guestRestrictions, guest.id); } catch (RemoteException re) { Log.w(TAG, "Could not update guest restrictions"); } diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 3f53ad4..5248131 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -45,18 +45,22 @@ public class AlwaysOnHotwordDetector { * Indicates that this hotword detector is no longer valid for any recognition * and should not be used anymore. */ - public static final int STATE_INVALID = -3; + private static final int STATE_INVALID = -3; + /** * Indicates that recognition for the given keyphrase is not available on the system * because of the hardware configuration. + * No further interaction should be performed with the detector that returns this availability. */ public static final int STATE_HARDWARE_UNAVAILABLE = -2; /** * Indicates that recognition for the given keyphrase is not supported. + * No further interaction should be performed with the detector that returns this availability. */ public static final int STATE_KEYPHRASE_UNSUPPORTED = -1; /** * Indicates that the given keyphrase is not enrolled. + * The caller may choose to begin an enrollment flow for the keyphrase. */ public static final int STATE_KEYPHRASE_UNENROLLED = 1; /** @@ -91,12 +95,14 @@ public class AlwaysOnHotwordDetector { // Must be kept in sync with the related attribute defined as searchKeyphraseRecognitionFlags. /** - * Simple recognition of the key phrase. Returned by {@link #getSupportedRecognitionModes()} + * Simple recognition of the key phrase. + * Returned by {@link #getSupportedRecognitionModes()} */ public static final int RECOGNITION_MODE_VOICE_TRIGGER = SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER; /** - * Trigger only if one user is identified. Returned by {@link #getSupportedRecognitionModes()} + * User identification performed with the keyphrase recognition. + * Returned by {@link #getSupportedRecognitionModes()} */ public static final int RECOGNITION_MODE_USER_IDENTIFICATION = SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION; @@ -149,16 +155,11 @@ public class AlwaysOnHotwordDetector { * * Availability implies whether the hardware on this system is capable of listening for * the given keyphrase or not. <p/> - * If the return code is one of {@link #STATE_HARDWARE_UNAVAILABLE} or - * {@link #STATE_KEYPHRASE_UNSUPPORTED}, - * detection is not possible and no further interaction should be - * performed with this detector. <br/> - * If it is {@link #STATE_KEYPHRASE_UNENROLLED} the caller may choose to begin - * an enrollment flow for the keyphrase. <br/> - * and for {@link #STATE_KEYPHRASE_ENROLLED} a recognition can be started as desired. <p/> * - * If the return code is {@link #STATE_INVALID}, this detector is stale. - * A new detector should be obtained for use in the future. + * @see AlwaysOnHotwordDetector#STATE_HARDWARE_UNAVAILABLE + * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNSUPPORTED + * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNENROLLED + * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_ENROLLED */ void onAvailabilityChanged(int status); /** @@ -217,9 +218,15 @@ public class AlwaysOnHotwordDetector { /** * Gets the recognition modes supported by the associated keyphrase. * + * @see #RECOGNITION_MODE_USER_IDENTIFICATION + * @see #RECOGNITION_MODE_VOICE_TRIGGER + * * @throws UnsupportedOperationException if the keyphrase itself isn't supported. * Callers should only call this method after a supported state callback on * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. + * @throws IllegalStateException if the detector is in an invalid state. + * This may happen if another detector has been instantiated or the + * {@link VoiceInteractionService} hosting this detector has been shut down. */ public int getSupportedRecognitionModes() { synchronized (mLock) { @@ -228,6 +235,11 @@ public class AlwaysOnHotwordDetector { } private int getSupportedRecognitionModesLocked() { + if (mAvailability == STATE_INVALID) { + throw new IllegalStateException( + "getSupportedRecognitionModes called on an invalid detector"); + } + // This method only makes sense if we can actually support a recognition. if (mAvailability != STATE_KEYPHRASE_ENROLLED && mAvailability != STATE_KEYPHRASE_UNENROLLED) { @@ -247,9 +259,16 @@ public class AlwaysOnHotwordDetector { * @throws UnsupportedOperationException if the recognition isn't supported. * Callers should only call this method after a supported state callback on * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. + * @throws IllegalStateException if the detector is in an invalid state. + * This may happen if another detector has been instantiated or the + * {@link VoiceInteractionService} hosting this detector has been shut down. */ public void startRecognition(int recognitionFlags) { synchronized (mLock) { + if (mAvailability == STATE_INVALID) { + throw new IllegalStateException("startRecognition called on an invalid detector"); + } + // Check if we can start/stop a recognition. if (mAvailability != STATE_KEYPHRASE_ENROLLED) { throw new UnsupportedOperationException( @@ -268,9 +287,16 @@ public class AlwaysOnHotwordDetector { * @throws UnsupportedOperationException if the recognition isn't supported. * Callers should only call this method after a supported state callback on * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. + * @throws IllegalStateException if the detector is in an invalid state. + * This may happen if another detector has been instantiated or the + * {@link VoiceInteractionService} hosting this detector has been shut down. */ public void stopRecognition() { synchronized (mLock) { + if (mAvailability == STATE_INVALID) { + throw new IllegalStateException("stopRecognition called on an invalid detector"); + } + // Check if we can start/stop a recognition. if (mAvailability != STATE_KEYPHRASE_ENROLLED) { throw new UnsupportedOperationException( @@ -293,14 +319,28 @@ public class AlwaysOnHotwordDetector { * @throws UnsupportedOperationException if managing they keyphrase isn't supported. * Callers should only call this method after a supported state callback on * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. + * @throws IllegalStateException if the detector is in an invalid state. + * This may happen if another detector has been instantiated or the + * {@link VoiceInteractionService} hosting this detector has been shut down. */ public Intent getManageIntent(int action) { + synchronized (mLock) { + return getManageIntentLocked(action); + } + } + + private Intent getManageIntentLocked(int action) { + if (mAvailability == STATE_INVALID) { + throw new IllegalStateException("getManageIntent called on an invalid detector"); + } + // This method only makes sense if we can actually support a recognition. if (mAvailability != STATE_KEYPHRASE_ENROLLED && mAvailability != STATE_KEYPHRASE_UNENROLLED) { throw new UnsupportedOperationException( "Managing the given keyphrase is not supported"); } + if (action != MANAGE_ACTION_ENROLL && action != MANAGE_ACTION_RE_ENROLL && action != MANAGE_ACTION_UN_ENROLL) { @@ -387,7 +427,6 @@ public class AlwaysOnHotwordDetector { if (DBG) Slog.d(TAG, "starting recognition..."); int status = startRecognitionLocked(); if (status == STATUS_OK) { - mInternalState |= FLAG_STARTED; mHandler.sendEmptyMessage(MSG_DETECTION_STARTED); } else { if (DBG) Slog.d(TAG, "failed to start recognition: " + status); @@ -404,7 +443,6 @@ public class AlwaysOnHotwordDetector { if (DBG) Slog.d(TAG, "stopping recognition..."); int status = stopRecognitionLocked(); if (status == STATUS_OK) { - mInternalState &= ~FLAG_STARTED; if (!requested) mHandler.sendEmptyMessage(MSG_DETECTION_STOPPED); } else { if (!requested) mHandler.sendEmptyMessage(MSG_DETECTION_ERROR); @@ -483,20 +521,42 @@ public class AlwaysOnHotwordDetector { class MyHandler extends Handler { @Override public void handleMessage(Message msg) { + synchronized (mLock) { + if (mAvailability == STATE_INVALID) { + Slog.w(TAG, "Received message: " + msg.what + " for an invalid detector"); + return; + } + } + switch (msg.what) { case MSG_STATE_CHANGED: mExternalCallback.onAvailabilityChanged(msg.arg1); break; case MSG_HOTWORD_DETECTED: + synchronized (mLock) { + mInternalState &= ~FLAG_REQUESTED; + mInternalState &= ~FLAG_STARTED; + } mExternalCallback.onDetected((byte[]) msg.obj); break; case MSG_DETECTION_STARTED: + synchronized (mLock) { + mInternalState |= FLAG_STARTED; + } mExternalCallback.onDetectionStarted(); break; case MSG_DETECTION_STOPPED: + synchronized (mLock) { + mInternalState &= ~FLAG_REQUESTED; + mInternalState &= ~FLAG_STARTED; + } mExternalCallback.onDetectionStopped(); break; case MSG_DETECTION_ERROR: + synchronized (mLock) { + mInternalState &= ~FLAG_REQUESTED; + mInternalState &= ~FLAG_STARTED; + } mExternalCallback.onError(); break; default: diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java index f3a48a6..cb447fd 100644 --- a/core/java/android/text/style/TtsSpan.java +++ b/core/java/android/text/style/TtsSpan.java @@ -518,7 +518,7 @@ public class TtsSpan implements ParcelableSpan { * This class uses generics so methods from this class can return instances * of its child classes, resulting in a fluent API (CRTP pattern). */ - public static abstract class Builder<C extends Builder<C>> { + public static class Builder<C extends Builder<?>> { // Holds the type of this class. private final String mType; @@ -580,7 +580,7 @@ public class TtsSpan implements ParcelableSpan { * this builder like {@link TtsSpan.TextBuilder} and * {@link TtsSpan.CardinalBuilder} are likely more useful. */ - public static class SemioticClassBuilder<C extends SemioticClassBuilder<C>> + public static class SemioticClassBuilder<C extends SemioticClassBuilder<?>> extends Builder<C> { public SemioticClassBuilder(String type) { diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index e9ec565..eee4973 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -355,9 +355,10 @@ public class RenderNode { return nSetOutlineEmpty(mNativeRenderNode); } else if (outline.mRect != null) { return nSetOutlineRoundRect(mNativeRenderNode, outline.mRect.left, outline.mRect.top, - outline.mRect.right, outline.mRect.bottom, outline.mRadius); + outline.mRect.right, outline.mRect.bottom, outline.mRadius, outline.mAlpha); } else if (outline.mPath != null) { - return nSetOutlineConvexPath(mNativeRenderNode, outline.mPath.mNativePath); + return nSetOutlineConvexPath(mNativeRenderNode, outline.mPath.mNativePath, + outline.mAlpha); } throw new IllegalArgumentException("Unrecognized outline?"); } @@ -849,8 +850,9 @@ public class RenderNode { private static native boolean nSetProjectBackwards(long renderNode, boolean shouldProject); private static native boolean nSetProjectionReceiver(long renderNode, boolean shouldRecieve); private static native boolean nSetOutlineRoundRect(long renderNode, int left, int top, - int right, int bottom, float radius); - private static native boolean nSetOutlineConvexPath(long renderNode, long nativePath); + int right, int bottom, float radius, float alpha); + private static native boolean nSetOutlineConvexPath(long renderNode, long nativePath, + float alpha); private static native boolean nSetOutlineEmpty(long renderNode); private static native boolean nSetOutlineNone(long renderNode); private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline); diff --git a/core/java/android/view/ViewOutlineProvider.java b/core/java/android/view/ViewOutlineProvider.java index 64624ae..4054031 100644 --- a/core/java/android/view/ViewOutlineProvider.java +++ b/core/java/android/view/ViewOutlineProvider.java @@ -25,7 +25,8 @@ import android.graphics.drawable.Drawable; public abstract class ViewOutlineProvider { /** * Default outline provider for Views, which queries the Outline from the View's background, - * or returns <code>false</code> if the View does not have a background. + * or generates a 0 alpha, rectangular Outline the size of the View if a background + * isn't present. * * @see Drawable#getOutline(Outline) */ @@ -35,6 +36,10 @@ public abstract class ViewOutlineProvider { Drawable background = view.getBackground(); if (background != null) { background.getOutline(outline); + } else { + + outline.setRect(0, 0, view.getWidth(), view.getHeight()); + outline.setAlpha(0.0f); } } }; diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index c169d35..e7b3152 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -1425,6 +1425,21 @@ public abstract class Window { public void setEnterTransition(Transition transition) {} /** + * Sets the Transition that will be used to move Views out of the scene when the Window is + * preparing to close, for example after a call to + * {@link android.app.Activity#finishAfterTransition()}. The exiting + * Views will be those that are regular Views or ViewGroups that have + * {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend + * {@link android.transition.Visibility} as entering is governed by changing visibility from + * {@link View#VISIBLE} to {@link View#INVISIBLE}. If <code>transition</code> is null, + * entering Views will remain unaffected. If nothing is set, the default will be to + * use the same value as set in {@link #setEnterTransition(android.transition.Transition)}. + * @param transition The Transition to use to move Views out of the Scene when the Window + * is preparing to close. + */ + public void setReturnTransition(Transition transition) {} + + /** * Sets the Transition that will be used to move Views out of the scene when starting a * new Activity. The exiting Views will be those that are regular Views or ViewGroups that * have {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend @@ -1437,6 +1452,20 @@ public abstract class Window { public void setExitTransition(Transition transition) {} /** + * Sets the Transition that will be used to move Views in to the scene when returning from + * a previously-started Activity. The entering Views will be those that are regular Views + * or ViewGroups that have {@link ViewGroup#isTransitionGroup} return true. Typical Transitions + * will extend {@link android.transition.Visibility} as exiting is governed by changing + * visibility from {@link View#VISIBLE} to {@link View#INVISIBLE}. If transition is null, + * the views will remain unaffected. If nothing is set, the default will be to use the same + * transition as {@link #setExitTransition(android.transition.Transition)}. + * Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * @param transition The Transition to use to move Views into the scene when reentering from a + * previously-started Activity. + */ + public void setReenterTransition(Transition transition) {} + + /** * Returns the transition used to move Views into the initial scene. The entering * Views will be those that are regular Views or ViewGroups that have * {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend @@ -1449,6 +1478,19 @@ public abstract class Window { public Transition getEnterTransition() { return null; } /** + * Returns he Transition that will be used to move Views out of the scene when the Window is + * preparing to close, for example after a call to + * {@link android.app.Activity#finishAfterTransition()}. The exiting + * Views will be those that are regular Views or ViewGroups that have + * {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend + * {@link android.transition.Visibility} as entering is governed by changing visibility from + * {@link View#VISIBLE} to {@link View#INVISIBLE}. + * @return The Transition to use to move Views out of the Scene when the Window + * is preparing to close. + */ + public Transition getReturnTransition() { return null; } + + /** * Returns the Transition that will be used to move Views out of the scene when starting a * new Activity. The exiting Views will be those that are regular Views or ViewGroups that * have {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend @@ -1461,6 +1503,18 @@ public abstract class Window { public Transition getExitTransition() { return null; } /** + * Returns the Transition that will be used to move Views in to the scene when returning from + * a previously-started Activity. The entering Views will be those that are regular Views + * or ViewGroups that have {@link ViewGroup#isTransitionGroup} return true. Typical Transitions + * will extend {@link android.transition.Visibility} as exiting is governed by changing + * visibility from {@link View#VISIBLE} to {@link View#INVISIBLE}. + * Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * @return The Transition to use to move Views into the scene when reentering from a + * previously-started Activity. + */ + public Transition getReenterTransition() { return null; } + + /** * Sets the Transition that will be used for shared elements transferred into the content * Scene. Typical Transitions will affect size and location, such as * {@link android.transition.ChangeBounds}. A null @@ -1472,6 +1526,19 @@ public abstract class Window { public void setSharedElementEnterTransition(Transition transition) {} /** + * Sets the Transition that will be used for shared elements transferred back to a + * calling Activity. Typical Transitions will affect size and location, such as + * {@link android.transition.ChangeBounds}. A null + * value will cause transferred shared elements to blink to the final position. + * If no value is set, the default will be to use the same value as + * {@link #setSharedElementEnterTransition(android.transition.Transition)}. + * Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * @param transition The Transition to use for shared elements transferred out of the content + * Scene. + */ + public void setSharedElementReturnTransition(Transition transition) {} + + /** * Returns the Transition that will be used for shared elements transferred into the content * Scene. Requires {@link #FEATURE_CONTENT_TRANSITIONS}. * @return Transition to use for sharend elements transferred into the content Scene. @@ -1479,6 +1546,13 @@ public abstract class Window { public Transition getSharedElementEnterTransition() { return null; } /** + * Returns the Transition that will be used for shared elements transferred back to a + * calling Activity. Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * @return Transition to use for sharend elements transferred into the content Scene. + */ + public Transition getSharedElementReturnTransition() { return null; } + + /** * Sets the Transition that will be used for shared elements after starting a new Activity * before the shared elements are transferred to the called Activity. If the shared elements * must animate during the exit transition, this Transition should be used. Upon completion, @@ -1490,6 +1564,17 @@ public abstract class Window { public void setSharedElementExitTransition(Transition transition) {} /** + * Sets the Transition that will be used for shared elements reentering from a started + * Activity after it has returned the shared element to it start location. If no value + * is set, this will default to + * {@link #setSharedElementExitTransition(android.transition.Transition)}. + * Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * @param transition The Transition to use for shared elements in the launching Window + * after the shared element has returned to the Window. + */ + public void setSharedElementReenterTransition(Transition transition) {} + + /** * Returns the Transition to use for shared elements in the launching Window prior * to transferring to the launched Activity's Window. * Requires {@link #FEATURE_CONTENT_TRANSITIONS}. @@ -1500,6 +1585,16 @@ public abstract class Window { public Transition getSharedElementExitTransition() { return null; } /** + * Returns the Transition that will be used for shared elements reentering from a started + * Activity after it has returned the shared element to it start location. + * Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * + * @return the Transition that will be used for shared elements reentering from a started + * Activity after it has returned the shared element to it start location. + */ + public Transition getSharedElementReenterTransition() { return null; } + + /** * Controls how the transition set in * {@link #setEnterTransition(android.transition.Transition)} overlaps with the exit * transition of the calling Activity. When true, the transition will start as soon as possible. diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 2c7b3eb..2e836fb 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -16,16 +16,26 @@ package android.webkit; +import android.app.ActivityManagerInternal; +import android.app.Application; +import android.app.AppGlobals; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.os.Build; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; +import android.os.SystemProperties; +import android.text.TextUtils; import android.util.AndroidRuntimeException; import android.util.Log; +import com.android.server.LocalServices; import dalvik.system.VMRuntime; import java.io.File; +import java.util.Arrays; import com.android.internal.os.Zygote; @@ -42,17 +52,15 @@ public final class WebViewFactory { private static final String NULL_WEBVIEW_FACTORY = "com.android.webview.nullwebview.NullWebViewFactoryProvider"; - // TODO(torne): we need to use a system property instead of hardcoding the library paths to - // enable it to be changed when a webview update apk is installed. - private static final String CHROMIUM_WEBVIEW_NATIVE_LIB_32 = - "/system/lib/libwebviewchromium.so"; - private static final String CHROMIUM_WEBVIEW_NATIVE_LIB_64 = - "/system/lib64/libwebviewchromium.so"; private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_32 = "/data/misc/shared_relro/libwebviewchromium32.relro"; private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_64 = "/data/misc/shared_relro/libwebviewchromium64.relro"; + public static final String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY = + "persist.sys.webview.vmsize"; + private static final long CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES = 100 * 1024 * 1024; + private static final String LOGTAG = "WebViewFactory"; private static final boolean DEBUG = false; @@ -64,8 +72,8 @@ public final class WebViewFactory { private static boolean sAddressSpaceReserved = false; public static String getWebViewPackageName() { - // TODO: Make this dynamic based on resource configuration. - return "com.android.webview"; + return AppGlobals.getInitialApplication().getString( + com.android.internal.R.string.config_webViewPackageName); } static WebViewFactoryProvider getProvider() { @@ -99,10 +107,18 @@ public final class WebViewFactory { } private static Class<WebViewFactoryProvider> getFactoryClass() throws ClassNotFoundException { + Application initialApplication = AppGlobals.getInitialApplication(); try { - return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY); - } catch (ClassNotFoundException e) { - Log.e(LOGTAG, "Chromium WebView does not exist"); + Context webViewContext = initialApplication.createPackageContext( + getWebViewPackageName(), + Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); + initialApplication.getAssets().addAssetPath( + webViewContext.getApplicationInfo().sourceDir); + ClassLoader clazzLoader = webViewContext.getClassLoader(); + return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY, true, + clazzLoader); + } catch (PackageManager.NameNotFoundException e) { + Log.e(LOGTAG, "Chromium WebView package does not exist"); return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY); } } @@ -114,79 +130,197 @@ public final class WebViewFactory { public static void prepareWebViewInZygote() { try { System.loadLibrary("webviewchromium_loader"); - sAddressSpaceReserved = nativeReserveAddressSpace(CHROMIUM_WEBVIEW_NATIVE_LIB_32, - CHROMIUM_WEBVIEW_NATIVE_LIB_64); + long addressSpaceToReserve = + SystemProperties.getLong(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY, + CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES); + sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve); + if (sAddressSpaceReserved) { - if (DEBUG) Log.v(LOGTAG, "address space reserved"); + if (DEBUG) { + Log.v(LOGTAG, "address space reserved: " + addressSpaceToReserve + " bytes"); + } } else { - Log.e(LOGTAG, "reserving address space failed"); + Log.e(LOGTAG, "reserving " + addressSpaceToReserve + + " bytes of address space failed"); } - } catch (Throwable e) { + } catch (Throwable t) { // Log and discard errors at this stage as we must not crash the zygote. - Log.e(LOGTAG, "error preparing native loader", e); + Log.e(LOGTAG, "error preparing native loader", t); } } /** * Perform any WebView loading preparations that must happen at boot from the system server, - * after the package manager has started. + * after the package manager has started or after an update to the webview is installed. * This must be called in the system server. * Currently, this means spawning the child processes which will create the relro files. */ public static void prepareWebViewInSystemServer() { + String[] nativePaths = null; + try { + nativePaths = getWebViewNativeLibraryPaths(); + } catch (PackageManager.NameNotFoundException e) { + } + prepareWebViewInSystemServer(nativePaths); + } + + private static void prepareWebViewInSystemServer(String[] nativeLibraryPaths) { if (DEBUG) Log.v(LOGTAG, "creating relro files"); - if (new File(CHROMIUM_WEBVIEW_NATIVE_LIB_64).exists()) { - createRelroFile(Build.SUPPORTED_64_BIT_ABIS[0]); + + // We must always trigger createRelRo regardless of the value of nativeLibraryPaths. Any + // unexpected values will be handled there to ensure that we trigger notifying any process + // waiting on relreo creation. + if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { + if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro"); + createRelroFile(false /* is64Bit */, nativeLibraryPaths); + } + + if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { + if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro"); + createRelroFile(true /* is64Bit */, nativeLibraryPaths); } - if (new File(CHROMIUM_WEBVIEW_NATIVE_LIB_32).exists()) { - createRelroFile(Build.SUPPORTED_32_BIT_ABIS[0]); + } + + public static void onWebViewUpdateInstalled() { + String[] nativeLibs = null; + try { + nativeLibs = WebViewFactory.getWebViewNativeLibraryPaths(); + } catch (PackageManager.NameNotFoundException e) { + } + + if (nativeLibs != null) { + long newVmSize = 0L; + + for (String path : nativeLibs) { + if (DEBUG) Log.d(LOGTAG, "Checking file size of " + path); + if (path == null) continue; + File f = new File(path); + if (f.exists()) { + long length = f.length(); + if (length > newVmSize) { + newVmSize = length; + } + } + } + + if (DEBUG) { + Log.v(LOGTAG, "Based on library size, need " + newVmSize + + " bytes of address space."); + } + // The required memory can be larger than the file on disk (due to .bss), and an + // upgraded version of the library will likely be larger, so always attempt to reserve + // twice as much as we think to allow for the library to grow during this boot cycle. + newVmSize = Math.max(2 * newVmSize, CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES); + Log.d(LOGTAG, "Setting new address space to " + newVmSize); + SystemProperties.set(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY, + Long.toString(newVmSize)); + } + prepareWebViewInSystemServer(nativeLibs); + } + + private static String[] getWebViewNativeLibraryPaths() + throws PackageManager.NameNotFoundException { + final String NATIVE_LIB_FILE_NAME = "libwebviewchromium.so"; + + PackageManager pm = AppGlobals.getInitialApplication().getPackageManager(); + ApplicationInfo ai = pm.getApplicationInfo(getWebViewPackageName(), 0); + + String path32; + String path64; + boolean primaryArchIs64bit = VMRuntime.is64BitAbi(ai.primaryCpuAbi); + if (!TextUtils.isEmpty(ai.secondaryCpuAbi)) { + // Multi-arch case. + if (primaryArchIs64bit) { + // Primary arch: 64-bit, secondary: 32-bit. + path64 = ai.nativeLibraryDir; + path32 = ai.secondaryNativeLibraryDir; + } else { + // Primary arch: 32-bit, secondary: 64-bit. + path64 = ai.secondaryNativeLibraryDir; + path32 = ai.nativeLibraryDir; + } + } else if (primaryArchIs64bit) { + // Single-arch 64-bit. + path64 = ai.nativeLibraryDir; + path32 = ""; + } else { + // Single-arch 32-bit. + path32 = ai.nativeLibraryDir; + path64 = ""; } + if (!TextUtils.isEmpty(path32)) path32 += "/" + NATIVE_LIB_FILE_NAME; + if (!TextUtils.isEmpty(path64)) path64 += "/" + NATIVE_LIB_FILE_NAME; + return new String[] { path32, path64 }; } - private static void createRelroFile(String abi) { + private static void createRelroFile(final boolean is64Bit, String[] nativeLibraryPaths) { + final String abi = + is64Bit ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]; + + // crashHandler is invoked by the ActivityManagerService when the isolated process crashes. + Runnable crashHandler = new Runnable() { + @Override + public void run() { + try { + Log.e(LOGTAG, "relro file creator for " + abi + " crashed. Proceeding without"); + getUpdateService().notifyRelroCreationCompleted(is64Bit, false); + } catch (RemoteException e) { + Log.e(LOGTAG, "Cannot reach WebViewUpdateService. " + e.getMessage()); + } + } + }; + try { - Process.start("android.webkit.WebViewFactory$RelroFileCreator", - "WebViewLoader-" + abi, - Process.SHARED_RELRO_UID, - Process.SHARED_RELRO_UID, - null, - 0, // TODO(torne): do we need to set debug flags? - Zygote.MOUNT_EXTERNAL_NONE, - Build.VERSION.SDK_INT, - null, - abi, - null); - } catch (Throwable e) { + if (nativeLibraryPaths == null + || nativeLibraryPaths[0] == null || nativeLibraryPaths[1] == null) { + throw new IllegalArgumentException( + "Native library paths to the WebView RelRo process must not be null!"); + } + int pid = LocalServices.getService(ActivityManagerInternal.class).startIsolatedProcess( + RelroFileCreator.class.getName(), nativeLibraryPaths, "WebViewLoader-" + abi, abi, + Process.SHARED_RELRO_UID, crashHandler); + if (pid <= 0) throw new Exception("Failed to start the relro file creator process"); + } catch (Throwable t) { // Log and discard errors as we must not crash the system server. - Log.e(LOGTAG, "error starting relro file creator for abi " + abi, e); + Log.e(LOGTAG, "error starting relro file creator for abi " + abi, t); + crashHandler.run(); } } private static class RelroFileCreator { // Called in an unprivileged child process to create the relro file. public static void main(String[] args) { - if (!sAddressSpaceReserved) { - Log.e(LOGTAG, "can't create relro file; address space not reserved"); - return; - } - boolean result = nativeCreateRelroFile(CHROMIUM_WEBVIEW_NATIVE_LIB_32, - CHROMIUM_WEBVIEW_NATIVE_LIB_64, - CHROMIUM_WEBVIEW_NATIVE_RELRO_32, - CHROMIUM_WEBVIEW_NATIVE_RELRO_64); - if (!result) { - Log.e(LOGTAG, "failed to create relro file"); - } else if (DEBUG) { - Log.v(LOGTAG, "created relro file"); - } - try { - getUpdateService().notifyRelroCreationCompleted(VMRuntime.getRuntime().is64Bit(), - result); - } catch (RemoteException e) { - Log.e(LOGTAG, "error notifying update service", e); - } + boolean result = false; + boolean is64Bit = VMRuntime.getRuntime().is64Bit(); + try{ + if (args.length != 2 || args[0] == null || args[1] == null) { + Log.e(LOGTAG, "Invalid RelroFileCreator args: " + Arrays.toString(args)); + return; + } + Log.v(LOGTAG, "RelroFileCreator (64bit = " + is64Bit + "), " + + " 32-bit lib: " + args[0] + ", 64-bit lib: " + args[1]); + if (!sAddressSpaceReserved) { + Log.e(LOGTAG, "can't create relro file; address space not reserved"); + return; + } + result = nativeCreateRelroFile(args[0] /* path32 */, + args[1] /* path64 */, + CHROMIUM_WEBVIEW_NATIVE_RELRO_32, + CHROMIUM_WEBVIEW_NATIVE_RELRO_64); + if (result && DEBUG) Log.v(LOGTAG, "created relro file"); + } finally { + // We must do our best to always notify the update service, even if something fails. + try { + getUpdateService().notifyRelroCreationCompleted(is64Bit, result); + } catch (RemoteException e) { + Log.e(LOGTAG, "error notifying update service", e); + } + + if (!result) Log.e(LOGTAG, "failed to create relro file"); - // Must explicitly exit or else this process will just sit around after we return. - System.exit(0); + // Must explicitly exit or else this process will just sit around after we return. + System.exit(0); + } } } @@ -203,14 +337,19 @@ public final class WebViewFactory { return; } - boolean result = nativeLoadWithRelroFile(CHROMIUM_WEBVIEW_NATIVE_LIB_32, - CHROMIUM_WEBVIEW_NATIVE_LIB_64, - CHROMIUM_WEBVIEW_NATIVE_RELRO_32, - CHROMIUM_WEBVIEW_NATIVE_RELRO_64); - if (!result) { - Log.w(LOGTAG, "failed to load with relro file, proceeding without"); - } else if (DEBUG) { - Log.v(LOGTAG, "loaded with relro file"); + try { + String[] args = getWebViewNativeLibraryPaths(); + boolean result = nativeLoadWithRelroFile(args[0] /* path32 */, + args[1] /* path64 */, + CHROMIUM_WEBVIEW_NATIVE_RELRO_32, + CHROMIUM_WEBVIEW_NATIVE_RELRO_64); + if (!result) { + Log.w(LOGTAG, "failed to load with relro file, proceeding without"); + } else if (DEBUG) { + Log.v(LOGTAG, "loaded with relro file"); + } + } catch (PackageManager.NameNotFoundException e) { + Log.e(LOGTAG, "Failed to list WebView package libraries for loadNativeLibrary", e); } } @@ -218,7 +357,7 @@ public final class WebViewFactory { return IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate")); } - private static native boolean nativeReserveAddressSpace(String lib32, String lib64); + private static native boolean nativeReserveAddressSpace(long addressSpaceToReserve); private static native boolean nativeCreateRelroFile(String lib32, String lib64, String relro32, String relro64); private static native boolean nativeLoadWithRelroFile(String lib32, String lib64, diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java index dca9921..7db70ba 100644 --- a/core/java/com/android/internal/util/XmlUtils.java +++ b/core/java/com/android/internal/util/XmlUtils.java @@ -16,12 +16,18 @@ package com.android.internal.util; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Bitmap.CompressFormat; +import android.net.Uri; +import android.util.Base64; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -1415,6 +1421,20 @@ public class XmlUtils { out.attribute(null, name, Long.toString(value)); } + public static float readFloatAttribute(XmlPullParser in, String name) throws IOException { + final String value = in.getAttributeValue(null, name); + try { + return Float.parseFloat(value); + } catch (NumberFormatException e) { + throw new ProtocolException("problem parsing " + name + "=" + value + " as long"); + } + } + + public static void writeFloatAttribute(XmlSerializer out, String name, float value) + throws IOException { + out.attribute(null, name, Float.toString(value)); + } + public static boolean readBooleanAttribute(XmlPullParser in, String name) { final String value = in.getAttributeValue(null, name); return Boolean.parseBoolean(value); @@ -1425,6 +1445,63 @@ public class XmlUtils { out.attribute(null, name, Boolean.toString(value)); } + public static Uri readUriAttribute(XmlPullParser in, String name) { + final String value = in.getAttributeValue(null, name); + return (value != null) ? Uri.parse(value) : null; + } + + public static void writeUriAttribute(XmlSerializer out, String name, Uri value) + throws IOException { + if (value != null) { + out.attribute(null, name, value.toString()); + } + } + + public static String readStringAttribute(XmlPullParser in, String name) { + return in.getAttributeValue(null, name); + } + + public static void writeStringAttribute(XmlSerializer out, String name, String value) + throws IOException { + if (value != null) { + out.attribute(null, name, value); + } + } + + public static byte[] readByteArrayAttribute(XmlPullParser in, String name) { + final String value = in.getAttributeValue(null, name); + if (value != null) { + return Base64.decode(value, Base64.DEFAULT); + } else { + return null; + } + } + + public static void writeByteArrayAttribute(XmlSerializer out, String name, byte[] value) + throws IOException { + if (value != null) { + out.attribute(null, name, Base64.encodeToString(value, Base64.DEFAULT)); + } + } + + public static Bitmap readBitmapAttribute(XmlPullParser in, String name) { + final byte[] value = readByteArrayAttribute(in, name); + if (value != null) { + return BitmapFactory.decodeByteArray(value, 0, value.length); + } else { + return null; + } + } + + public static void writeBitmapAttribute(XmlSerializer out, String name, Bitmap value) + throws IOException { + if (value != null) { + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + value.compress(CompressFormat.PNG, 90, os); + writeByteArrayAttribute(out, name, os.toByteArray()); + } + } + /** @hide */ public interface WriteMapCallback { /** diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java index 790b611..9e24844 100644 --- a/core/java/com/android/internal/widget/ActionBarContainer.java +++ b/core/java/com/android/internal/widget/ActionBarContainer.java @@ -16,10 +16,12 @@ package com.android.internal.widget; +import android.annotation.NonNull; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.Outline; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.ActionMode; @@ -324,18 +326,36 @@ public class ActionBarContainer extends FrameLayout { * projection surfaces. */ private class ActionBarBackgroundDrawable extends Drawable { - @Override - public void draw(Canvas canvas) { + private Drawable getDrawable() { if (mIsSplit) { - if (mSplitBackground != null) mSplitBackground.draw(canvas); + if (mSplitBackground != null) { + return mSplitBackground; + } } else { if (mBackground != null) { - mBackground.draw(canvas); + return mBackground; } if (mStackedBackground != null && mIsStacked) { - mStackedBackground.draw(canvas); + return mStackedBackground; } } + return null; + } + + @Override + public void draw(Canvas canvas) { + final Drawable drawable = getDrawable(); + if (drawable != null) { + drawable.draw(canvas); + } + } + + @Override + public void getOutline(@NonNull Outline outline) { + final Drawable drawable = getDrawable(); + if (drawable != null) { + drawable.getOutline(outline); + } } @Override |