Merge "Fix ObservableScrollView's cancel dispatch" into mnc-dev
diff --git a/Android.mk b/Android.mk
index 146afe0..59c1878 100644
--- a/Android.mk
+++ b/Android.mk
@@ -146,6 +146,7 @@
core/java/android/database/IContentObserver.aidl \
core/java/android/hardware/ICameraService.aidl \
core/java/android/hardware/ICameraServiceListener.aidl \
+ core/java/android/hardware/ICameraServiceProxy.aidl \
core/java/android/hardware/ICamera.aidl \
core/java/android/hardware/ICameraClient.aidl \
core/java/android/hardware/IConsumerIrService.aidl \
@@ -155,6 +156,8 @@
core/java/android/hardware/display/IDisplayManager.aidl \
core/java/android/hardware/display/IDisplayManagerCallback.aidl \
core/java/android/hardware/display/IVirtualDisplayCallback.aidl \
+ core/java/android/hardware/fingerprint/IFingerprintDaemon.aidl \
+ core/java/android/hardware/fingerprint/IFingerprintDaemonCallback.aidl \
core/java/android/hardware/fingerprint/IFingerprintService.aidl \
core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl \
core/java/android/hardware/hdmi/IHdmiControlCallback.aidl \
diff --git a/api/current.txt b/api/current.txt
index d3b8724..bbdb878 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5773,7 +5773,7 @@
method public void setPasswordMinimumSymbols(android.content.ComponentName, int);
method public void setPasswordMinimumUpperCase(android.content.ComponentName, int);
method public void setPasswordQuality(android.content.ComponentName, int);
- method public boolean setPermissionGranted(android.content.ComponentName, java.lang.String, java.lang.String, boolean);
+ method public boolean setPermissionGrantState(android.content.ComponentName, java.lang.String, java.lang.String, int);
method public void setPermissionPolicy(android.content.ComponentName, int);
method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List<java.lang.String>);
method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>);
@@ -5862,6 +5862,9 @@
field public static final int PASSWORD_QUALITY_NUMERIC_COMPLEX = 196608; // 0x30000
field public static final int PASSWORD_QUALITY_SOMETHING = 65536; // 0x10000
field public static final int PASSWORD_QUALITY_UNSPECIFIED = 0; // 0x0
+ field public static final int PERMISSION_GRANT_STATE_DEFAULT = 0; // 0x0
+ field public static final int PERMISSION_GRANT_STATE_DENIED = 2; // 0x2
+ field public static final int PERMISSION_GRANT_STATE_GRANTED = 1; // 0x1
field public static final int PERMISSION_POLICY_AUTO_DENY = 2; // 0x2
field public static final int PERMISSION_POLICY_AUTO_GRANT = 1; // 0x1
field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
@@ -7160,6 +7163,7 @@
public static final class ScanSettings.Builder {
ctor public ScanSettings.Builder();
method public android.bluetooth.le.ScanSettings build();
+ method public android.bluetooth.le.ScanSettings.Builder setCallbackType(int);
method public android.bluetooth.le.ScanSettings.Builder setMatchMode(int);
method public android.bluetooth.le.ScanSettings.Builder setNumOfMatches(int);
method public android.bluetooth.le.ScanSettings.Builder setReportDelay(long);
@@ -14797,8 +14801,8 @@
ctor public AudioFormat.Builder();
ctor public AudioFormat.Builder(android.media.AudioFormat);
method public android.media.AudioFormat build();
- method public android.media.AudioFormat.Builder setChannelIndexMask(int) throws java.lang.IllegalArgumentException;
- method public android.media.AudioFormat.Builder setChannelMask(int) throws java.lang.IllegalArgumentException;
+ method public android.media.AudioFormat.Builder setChannelIndexMask(int);
+ method public android.media.AudioFormat.Builder setChannelMask(int);
method public android.media.AudioFormat.Builder setEncoding(int) throws java.lang.IllegalArgumentException;
method public android.media.AudioFormat.Builder setSampleRate(int) throws java.lang.IllegalArgumentException;
}
@@ -14960,11 +14964,11 @@
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getAudioSource();
+ method public int getBufferSizeInFrames();
method public int getChannelConfiguration();
method public int getChannelCount();
method public android.media.AudioFormat getFormat();
method public static int getMinBufferSize(int, int, int);
- method public int getNativeFrameCount() throws java.lang.IllegalStateException;
method public int getNotificationMarkerPosition();
method public int getPositionNotificationPeriod();
method public android.media.AudioDeviceInfo getPreferredDevice();
@@ -15033,13 +15037,14 @@
method public void flush();
method public int getAudioFormat();
method public int getAudioSessionId();
+ method public int getBufferSizeInFrames();
method public int getChannelConfiguration();
method public int getChannelCount();
method public android.media.AudioFormat getFormat();
method public static float getMaxVolume();
method public static int getMinBufferSize(int, int, int);
method public static float getMinVolume();
- method public int getNativeFrameCount() throws java.lang.IllegalStateException;
+ method protected deprecated int getNativeFrameCount();
method public static int getNativeOutputSampleRate(int);
method public int getNotificationMarkerPosition();
method public int getPlayState();
@@ -18396,7 +18401,7 @@
field public static final java.lang.String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
}
- public deprecated class ProxyInfo implements android.os.Parcelable {
+ public class ProxyInfo implements android.os.Parcelable {
method public static android.net.ProxyInfo buildDirectProxy(java.lang.String, int);
method public static android.net.ProxyInfo buildDirectProxy(java.lang.String, int, java.util.List<java.lang.String>);
method public static android.net.ProxyInfo buildPacProxy(android.net.Uri);
@@ -25175,13 +25180,6 @@
field public static final int TYPE_KEEP_TOGETHER = 1; // 0x1
}
- public static final class ContactsContract.Authorization {
- ctor public ContactsContract.Authorization();
- field public static final java.lang.String AUTHORIZATION_METHOD = "authorize";
- field public static final java.lang.String KEY_AUTHORIZED_URI = "authorized_uri";
- field public static final java.lang.String KEY_URI_TO_AUTHORIZE = "uri_to_authorize";
- }
-
protected static abstract interface ContactsContract.BaseSyncColumns {
field public static final java.lang.String SYNC1 = "sync1";
field public static final java.lang.String SYNC2 = "sync2";
@@ -25588,6 +25586,8 @@
}
protected static abstract interface ContactsContract.DataColumns {
+ field public static final java.lang.String CARRIER_PRESENCE = "carrier_presence";
+ field public static final int CARRIER_PRESENCE_VT_CAPABLE = 1; // 0x1
field public static final java.lang.String DATA1 = "data1";
field public static final java.lang.String DATA10 = "data10";
field public static final java.lang.String DATA11 = "data11";
@@ -25826,16 +25826,6 @@
field public static final android.net.Uri CONTENT_URI;
}
- public static final class ContactsContract.ProviderStatus {
- field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/provider_status";
- field public static final android.net.Uri CONTENT_URI;
- field public static final java.lang.String STATUS = "status";
- field public static final int STATUS_CHANGING_LOCALE = 3; // 0x3
- field public static final int STATUS_NORMAL = 0; // 0x0
- field public static final int STATUS_NO_ACCOUNTS_NO_CONTACTS = 4; // 0x4
- field public static final int STATUS_UPGRADING = 1; // 0x1
- }
-
public static final class ContactsContract.QuickContact {
ctor public ContactsContract.QuickContact();
method public static void showQuickContact(android.content.Context, android.view.View, android.net.Uri, int, java.lang.String[]);
@@ -30323,6 +30313,7 @@
method public android.net.Uri getSubscriptionAddress();
method public java.util.List<java.lang.String> getSupportedUriSchemes();
method public boolean hasCapabilities(int);
+ method public boolean isEnabled();
method public boolean supportsUriScheme(java.lang.String);
method public android.telecom.PhoneAccount.Builder toBuilder();
method public void writeToParcel(android.os.Parcel, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 1dc5325..1a3673d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5882,7 +5882,7 @@
method public void setPasswordMinimumSymbols(android.content.ComponentName, int);
method public void setPasswordMinimumUpperCase(android.content.ComponentName, int);
method public void setPasswordQuality(android.content.ComponentName, int);
- method public boolean setPermissionGranted(android.content.ComponentName, java.lang.String, java.lang.String, boolean);
+ method public boolean setPermissionGrantState(android.content.ComponentName, java.lang.String, java.lang.String, int);
method public void setPermissionPolicy(android.content.ComponentName, int);
method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List<java.lang.String>);
method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>);
@@ -5976,6 +5976,9 @@
field public static final int PASSWORD_QUALITY_NUMERIC_COMPLEX = 196608; // 0x30000
field public static final int PASSWORD_QUALITY_SOMETHING = 65536; // 0x10000
field public static final int PASSWORD_QUALITY_UNSPECIFIED = 0; // 0x0
+ field public static final int PERMISSION_GRANT_STATE_DEFAULT = 0; // 0x0
+ field public static final int PERMISSION_GRANT_STATE_DENIED = 2; // 0x2
+ field public static final int PERMISSION_GRANT_STATE_GRANTED = 1; // 0x1
field public static final int PERMISSION_POLICY_AUTO_DENY = 2; // 0x2
field public static final int PERMISSION_POLICY_AUTO_GRANT = 1; // 0x1
field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
@@ -16024,8 +16027,8 @@
ctor public AudioFormat.Builder();
ctor public AudioFormat.Builder(android.media.AudioFormat);
method public android.media.AudioFormat build();
- method public android.media.AudioFormat.Builder setChannelIndexMask(int) throws java.lang.IllegalArgumentException;
- method public android.media.AudioFormat.Builder setChannelMask(int) throws java.lang.IllegalArgumentException;
+ method public android.media.AudioFormat.Builder setChannelIndexMask(int);
+ method public android.media.AudioFormat.Builder setChannelMask(int);
method public android.media.AudioFormat.Builder setEncoding(int) throws java.lang.IllegalArgumentException;
method public android.media.AudioFormat.Builder setSampleRate(int) throws java.lang.IllegalArgumentException;
}
@@ -16197,11 +16200,11 @@
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getAudioSource();
+ method public int getBufferSizeInFrames();
method public int getChannelConfiguration();
method public int getChannelCount();
method public android.media.AudioFormat getFormat();
method public static int getMinBufferSize(int, int, int);
- method public int getNativeFrameCount() throws java.lang.IllegalStateException;
method public int getNotificationMarkerPosition();
method public int getPositionNotificationPeriod();
method public android.media.AudioDeviceInfo getPreferredDevice();
@@ -16272,13 +16275,14 @@
method public void flush();
method public int getAudioFormat();
method public int getAudioSessionId();
+ method public int getBufferSizeInFrames();
method public int getChannelConfiguration();
method public int getChannelCount();
method public android.media.AudioFormat getFormat();
method public static float getMaxVolume();
method public static int getMinBufferSize(int, int, int);
method public static float getMinVolume();
- method public int getNativeFrameCount() throws java.lang.IllegalStateException;
+ method protected deprecated int getNativeFrameCount();
method public static int getNativeOutputSampleRate(int);
method public int getNotificationMarkerPosition();
method public int getPlayState();
@@ -19905,7 +19909,7 @@
field public static final java.lang.String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
}
- public deprecated class ProxyInfo implements android.os.Parcelable {
+ public class ProxyInfo implements android.os.Parcelable {
method public static android.net.ProxyInfo buildDirectProxy(java.lang.String, int);
method public static android.net.ProxyInfo buildDirectProxy(java.lang.String, int, java.util.List<java.lang.String>);
method public static android.net.ProxyInfo buildPacProxy(android.net.Uri);
@@ -27102,13 +27106,6 @@
field public static final int TYPE_KEEP_TOGETHER = 1; // 0x1
}
- public static final class ContactsContract.Authorization {
- ctor public ContactsContract.Authorization();
- field public static final java.lang.String AUTHORIZATION_METHOD = "authorize";
- field public static final java.lang.String KEY_AUTHORIZED_URI = "authorized_uri";
- field public static final java.lang.String KEY_URI_TO_AUTHORIZE = "uri_to_authorize";
- }
-
protected static abstract interface ContactsContract.BaseSyncColumns {
field public static final java.lang.String SYNC1 = "sync1";
field public static final java.lang.String SYNC2 = "sync2";
@@ -27515,6 +27512,8 @@
}
protected static abstract interface ContactsContract.DataColumns {
+ field public static final java.lang.String CARRIER_PRESENCE = "carrier_presence";
+ field public static final int CARRIER_PRESENCE_VT_CAPABLE = 1; // 0x1
field public static final java.lang.String DATA1 = "data1";
field public static final java.lang.String DATA10 = "data10";
field public static final java.lang.String DATA11 = "data11";
@@ -27753,16 +27752,6 @@
field public static final android.net.Uri CONTENT_URI;
}
- public static final class ContactsContract.ProviderStatus {
- field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/provider_status";
- field public static final android.net.Uri CONTENT_URI;
- field public static final java.lang.String STATUS = "status";
- field public static final int STATUS_CHANGING_LOCALE = 3; // 0x3
- field public static final int STATUS_NORMAL = 0; // 0x0
- field public static final int STATUS_NO_ACCOUNTS_NO_CONTACTS = 4; // 0x4
- field public static final int STATUS_UPGRADING = 1; // 0x1
- }
-
public static final class ContactsContract.QuickContact {
ctor public ContactsContract.QuickContact();
method public static void showQuickContact(android.content.Context, android.view.View, android.net.Uri, int, java.lang.String[]);
@@ -32509,6 +32498,7 @@
method public android.net.Uri getSubscriptionAddress();
method public java.util.List<java.lang.String> getSupportedUriSchemes();
method public boolean hasCapabilities(int);
+ method public boolean isEnabled();
method public boolean supportsUriScheme(java.lang.String);
method public android.telecom.PhoneAccount.Builder toBuilder();
method public void writeToParcel(android.os.Parcel, int);
@@ -32650,6 +32640,7 @@
method public void cancelMissedCallsNotification();
method public deprecated void clearAccounts();
method public void clearPhoneAccounts();
+ method public void enablePhoneAccount(android.telecom.PhoneAccountHandle, boolean);
method public boolean endCall();
method public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
method public java.util.List<android.telecom.PhoneAccountHandle> getAllPhoneAccountHandles();
@@ -32665,7 +32656,6 @@
method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(java.lang.String);
- method public java.util.List<android.telecom.PhoneAccountHandle> getRegisteredConnectionManagers();
method public android.telecom.PhoneAccountHandle getSimCallManager();
method public java.lang.String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
method public boolean handleMmi(java.lang.String);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3309443..96c6878 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1366,7 +1366,9 @@
int version = parcel.readInt();
when = parcel.readLong();
- mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
+ if (parcel.readInt() != 0) {
+ mSmallIcon = Icon.CREATOR.createFromParcel(parcel);
+ }
number = parcel.readInt();
if (parcel.readInt() != 0) {
contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
@@ -1590,7 +1592,12 @@
parcel.writeInt(1);
parcel.writeLong(when);
- mSmallIcon.writeToParcel(parcel, 0);
+ if (mSmallIcon != null) {
+ parcel.writeInt(1);
+ mSmallIcon.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
parcel.writeInt(number);
if (contentIntent != null) {
parcel.writeInt(1);
@@ -3241,7 +3248,7 @@
Notification n = new Notification();
n.when = mWhen;
n.mSmallIcon = mSmallIcon;
- if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
+ if (mSmallIcon != null && mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
n.icon = mSmallIcon.getResId();
}
n.iconLevel = mSmallIconLevel;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 55ff85a..a8f2311 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -825,6 +825,23 @@
*/
public static final int PERMISSION_POLICY_AUTO_DENY = 2;
+ /**
+ * Runtime permission state: The user can manage the permission
+ * through the UI.
+ */
+ public static final int PERMISSION_GRANT_STATE_DEFAULT = 0;
+
+ /**
+ * Runtime permission state: The permission is granted to the app
+ * and the user cannot manage the permission through the UI.
+ */
+ public static final int PERMISSION_GRANT_STATE_GRANTED = 1;
+
+ /**
+ * Runtime permission state: The permission is denied to the app
+ * and the user cannot manage the permission through the UI.
+ */
+ public static final int PERMISSION_GRANT_STATE_DENIED = 2;
/**
* Return true if the given administrator component is currently
@@ -4401,21 +4418,31 @@
}
/**
- * Grants or revokes a runtime permission to a specific application so that the user
- * does not have to be prompted. This might affect all permissions in a group that the
- * runtime permission belongs to. This method can only be called by a profile or device
- * owner.
+ * Sets the grant state of a runtime permission for a specific application. The state
+ * can be {@link #PERMISSION_GRANT_STATE_DEFAULT default} in which a user can manage it
+ * through the UI, {@link #PERMISSION_GRANT_STATE_DENIED denied}, in which the permission
+ * is denied and the user cannot manage it through the UI, and {@link
+ * #PERMISSION_GRANT_STATE_GRANTED granted} in which the permission is granted and the
+ * user cannot manage it through the UI. This might affect all permissions in a
+ * group that the runtime permission belongs to. This method can only be called
+ * by a profile or device owner.
+ *
* @param admin Which profile or device owner this request is associated with.
* @param packageName The application to grant or revoke a permission to.
* @param permission The permission to grant or revoke.
- * @param granted Whether or not to grant the permission. If false, all permissions in the
- * associated permission group will be denied.
- * @return whether the permission was successfully granted or revoked
+ * @param grantState The permission grant state which is one of {@link
+ * #PERMISSION_GRANT_STATE_DENIED}, {@link #PERMISSION_GRANT_STATE_DEFAULT},
+ * {@link #PERMISSION_GRANT_STATE_GRANTED},
+ * @return whether the permission was successfully granted or revoked.
+ *
+ * @see #PERMISSION_GRANT_STATE_DENIED
+ * @see #PERMISSION_GRANT_STATE_DEFAULT
+ * @see #PERMISSION_GRANT_STATE_GRANTED
*/
- public boolean setPermissionGranted(ComponentName admin, String packageName,
- String permission, boolean granted) {
+ public boolean setPermissionGrantState(ComponentName admin, String packageName,
+ String permission, int grantState) {
try {
- return mService.setPermissionGranted(admin, packageName, permission, granted);
+ return mService.setPermissionGrantState(admin, packageName, permission, grantState);
} catch (RemoteException re) {
Log.w(TAG, "Failed talking with device policy service", re);
return false;
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 24ef604..10b0941 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -234,6 +234,6 @@
void setPermissionPolicy(in ComponentName admin, int policy);
int getPermissionPolicy(in ComponentName admin);
- boolean setPermissionGranted(in ComponentName admin, String packageName, String permission,
- boolean granted);
+ boolean setPermissionGrantState(in ComponentName admin, String packageName,
+ String permission, int grantState);
}
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
index 4eeb577..d616624 100644
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -249,9 +249,7 @@
*
* @param callbackType The callback type flags for the scan.
* @throws IllegalArgumentException If the {@code callbackType} is invalid.
- * @hide
*/
- @SystemApi
public Builder setCallbackType(int callbackType) {
if (!isValidCallbackType(callbackType)) {
diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl
index 9201b614..c933f92 100644
--- a/core/java/android/hardware/ICameraService.aidl
+++ b/core/java/android/hardware/ICameraService.aidl
@@ -25,7 +25,11 @@
import android.hardware.ICameraServiceListener;
import android.hardware.CameraInfo;
-/** @hide */
+/**
+ * Binder interface for the native camera service running in mediaserver.
+ *
+ * @hide
+ */
interface ICameraService
{
/**
diff --git a/core/java/android/hardware/ICameraServiceProxy.aidl b/core/java/android/hardware/ICameraServiceProxy.aidl
new file mode 100644
index 0000000..0bb24bc
--- /dev/null
+++ b/core/java/android/hardware/ICameraServiceProxy.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/**
+ * Binder interface for the camera service proxy running in system_server.
+ *
+ * @hide
+ */
+interface ICameraServiceProxy
+{
+ /**
+ * Ping the service proxy to update the valid users for the camera service.
+ */
+ oneway void pingForUserUpdate();
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 9046e81..7f4a76c 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -67,8 +67,6 @@
private final TaskSingleDrainer mIdleDrainer;
/** Drain state transitions from BUSY -> IDLE */
private final TaskSingleDrainer mAbortDrainer;
- /** Drain the UNCONFIGURED state transition */
- private final TaskSingleDrainer mUnconfigureDrainer;
/** This session is closed; all further calls will throw ISE */
private boolean mClosed = false;
@@ -121,8 +119,6 @@
/*name*/"idle");
mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(),
/*name*/"abort");
- mUnconfigureDrainer = new TaskSingleDrainer(mDeviceHandler, new UnconfigureDrainListener(),
- /*name*/"unconf");
// CameraDevice should call configureOutputs and have it finish before constructing us
@@ -572,26 +568,6 @@
@Override
public void onUnconfigured(CameraDevice camera) {
if (VERBOSE) Log.v(TAG, mIdString + "onUnconfigured");
- synchronized (session) {
- // Ignore #onUnconfigured before #close is called.
- //
- // Normally, this is reached when this session is closed and no immediate other
- // activity happens for the camera, in which case the camera is configured to
- // null streams by this session and the UnconfigureDrainer task is started.
- // However, we can also end up here if
- //
- // 1) Session is closed
- // 2) New session is created before this session finishes closing, setting
- // mSkipUnconfigure and therefore this session does not configure null or
- // start the UnconfigureDrainer task.
- // 3) And then the new session fails to be created, so onUnconfigured fires
- // _anyway_.
- // In this second case, need to not finish a task that was never started, so
- // guard with mSkipUnconfigure
- if (mClosed && mConfigureSuccess && !mSkipUnconfigure) {
- mUnconfigureDrainer.taskFinished();
- }
- }
}
@Override
@@ -656,6 +632,19 @@
* then the drain immediately finishes.
*/
if (VERBOSE) Log.v(TAG, mIdString + "onSequenceDrained");
+
+
+ // Fire session close as soon as all sequences are complete.
+ // We may still need to unconfigure the device, but a new session might be created
+ // past this point, and notifications would then stop to this instance.
+ mStateCallback.onClosed(CameraCaptureSessionImpl.this);
+
+ // Fast path: A new capture session has replaced this one; don't wait for abort/idle
+ // as we won't get state updates any more anyway.
+ if (mSkipUnconfigure) {
+ return;
+ }
+
mAbortDrainer.beginDrain();
}
}
@@ -673,6 +662,12 @@
*
* If the camera is already "IDLE", then the drain immediately finishes.
*/
+
+ // Fast path: A new capture session has replaced this one; don't wait for idle
+ // as we won't get state updates any more anyway.
+ if (mSkipUnconfigure) {
+ return;
+ }
mIdleDrainer.beginDrain();
}
}
@@ -691,7 +686,7 @@
* The device is now IDLE, and has settled. It will not transition to
* ACTIVE or BUSY again by itself.
*
- * It's now safe to unconfigure the outputs and after it's done invoke #onClosed.
+ * It's now safe to unconfigure the outputs.
*
* This operation is idempotent; a session will not be closed twice.
*/
@@ -699,45 +694,31 @@
Log.v(TAG, mIdString + "Session drain complete, skip unconfigure: " +
mSkipUnconfigure);
- // Fast path: A new capture session has replaced this one; don't unconfigure.
+ // Fast path: A new capture session has replaced this one; don't wait for idle
+ // as we won't get state updates any more anyway.
if (mSkipUnconfigure) {
- mStateCallback.onClosed(CameraCaptureSessionImpl.this);
return;
}
- // Slow path: #close was called explicitly on this session; unconfigure first
- mUnconfigureDrainer.taskStarted();
-
+ // Final slow path: unconfigure the camera, no session has replaced us and
+ // everything is idle.
try {
// begin transition to unconfigured
mDeviceImpl.configureStreamsChecked(null, null);
} catch (CameraAccessException e) {
// OK: do not throw checked exceptions.
- Log.e(TAG, mIdString + "Exception while configuring outputs: ", e);
+ Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e);
// TODO: call onError instead of onClosed if this happens
} catch (IllegalStateException e) {
- // Camera is already closed, so go straight to the close callback
+ // Camera is already closed, so nothing left to do
if (VERBOSE) Log.v(TAG, mIdString +
"Camera was already closed or busy, skipping unconfigure");
- mUnconfigureDrainer.taskFinished();
}
- mUnconfigureDrainer.beginDrain();
}
}
}
}
- private class UnconfigureDrainListener implements TaskDrainer.DrainListener {
- @Override
-
- public void onDrained() {
- if (VERBOSE) Log.v(TAG, mIdString + "onUnconfigureDrained");
- synchronized (CameraCaptureSessionImpl.this) {
- // The device has finished unconfiguring. It's now fully closed.
- mStateCallback.onClosed(CameraCaptureSessionImpl.this);
- }
- }
- }
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 338bd76..caf21d5 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -729,12 +729,6 @@
}
}
- private void clearCallbacks() {
- mAuthenticationCallback = null;
- mEnrollmentCallback = null;
- mRemovalCallback = null;
- }
-
private void cancelEnrollment() {
if (mService != null) try {
mService.cancelEnrollment(mToken);
diff --git a/core/java/android/hardware/fingerprint/IFingerprintDaemon.aidl b/core/java/android/hardware/fingerprint/IFingerprintDaemon.aidl
new file mode 100644
index 0000000..186d36e
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/IFingerprintDaemon.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.fingerprint;
+
+import android.hardware.fingerprint.IFingerprintDaemonCallback;
+
+/**
+ * Communication channel from FingerprintService to FingerprintDaemon (fingerprintd)
+ * @hide
+ */
+
+interface IFingerprintDaemon {
+ int authenticate(long sessionId, int groupId);
+ int cancelAuthentication();
+ int enroll(in byte [] token, int groupId, int timeout);
+ int cancelEnrollment();
+ long preEnroll();
+ int remove(int fingerId, int groupId);
+ long getAuthenticatorId();
+ int setActiveGroup(int groupId, in byte[] path);
+ long openHal();
+ int closeHal();
+ void init(IFingerprintDaemonCallback callback);
+}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintDaemonCallback.aidl b/core/java/android/hardware/fingerprint/IFingerprintDaemonCallback.aidl
new file mode 100644
index 0000000..bd8ad6e
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/IFingerprintDaemonCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.fingerprint;
+
+/**
+ * Communication channel from the fingerprintd back to FingerprintService.
+ * @hide
+ */
+ interface IFingerprintDaemonCallback {
+ void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining);
+ void onAcquired(long deviceId, int acquiredInfo);
+ void onAuthenticated(long deviceId, int fingerId, int groupId);
+ void onError(long deviceId, int error);
+ void onRemoved(long deviceId, int fingerId, int groupId);
+ void onEnumerate(long deviceId, in int [] fingerIds, in int [] groupIds);
+}
diff --git a/core/java/android/net/IpReachabilityMonitor.java b/core/java/android/net/IpReachabilityMonitor.java
index add8774..b0f2003 100644
--- a/core/java/android/net/IpReachabilityMonitor.java
+++ b/core/java/android/net/IpReachabilityMonitor.java
@@ -73,7 +73,62 @@
private final Set<InetAddress> mIpWatchList;
private int mIpWatchListVersion;
private boolean mRunning;
- final private Thread mObserverThread;
+ private final NetlinkSocketObserver mNetlinkSocketObserver;
+ private final Thread mObserverThread;
+
+ /**
+ * Make the kernel to perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
+ * for the given IP address on the specified interface index.
+ *
+ * @return true, if the request was successfully passed to the kernel; false otherwise.
+ */
+ public static boolean probeNeighbor(int ifIndex, InetAddress ip) {
+ final long IO_TIMEOUT = 300L;
+ // This currently does not cause neighbor probing if the target |ip|
+ // has been confirmed reachable within the past "delay_probe_time"
+ // seconds, i.e. within the past 5 seconds.
+ //
+ // TODO: replace with a transition directly to NUD_PROBE state once
+ // kernels are updated to do so correctly.
+ if (DBG) { Log.d(TAG, "Probing ip=" + ip.getHostAddress()); }
+
+ final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
+ 1, ip, StructNdMsg.NUD_DELAY, ifIndex, null);
+ NetlinkSocket nlSocket = null;
+ boolean returnValue = false;
+
+ try {
+ nlSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
+ nlSocket.connectToKernel();
+ nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT);
+ final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT);
+ final NetlinkMessage response = NetlinkMessage.parse(bytes);
+ if (response != null && response instanceof NetlinkErrorMessage &&
+ (((NetlinkErrorMessage) response).getNlMsgError() != null) &&
+ (((NetlinkErrorMessage) response).getNlMsgError().error == 0)) {
+ returnValue = true;
+ } else {
+ String errmsg;
+ if (bytes == null) {
+ errmsg = "null recvMessage";
+ } else if (response == null) {
+ bytes.position(0);
+ errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes);
+ } else {
+ errmsg = response.toString();
+ }
+ Log.e(TAG, "Error probing ip=" + ip.getHostAddress() +
+ ", errmsg=" + errmsg);
+ }
+ } catch (ErrnoException | InterruptedIOException | SocketException e) {
+ Log.d(TAG, "Error probing ip=" + ip.getHostAddress(), e);
+ }
+
+ if (nlSocket != null) {
+ nlSocket.close();
+ }
+ return returnValue;
+ }
public IpReachabilityMonitor(String ifName, Callback callback) throws IllegalArgumentException {
mInterfaceName = ifName;
@@ -88,15 +143,15 @@
mIpWatchList = new HashSet<InetAddress>();
mIpWatchListVersion = 0;
mRunning = false;
- mObserverThread = new Thread(new NetlinkSocketObserver());
+ mNetlinkSocketObserver = new NetlinkSocketObserver();
+ mObserverThread = new Thread(mNetlinkSocketObserver);
mObserverThread.start();
}
public void stop() {
- synchronized (mLock) {
- mRunning = false;
- mIpWatchList.clear();
- }
+ synchronized (mLock) { mRunning = false; }
+ clearLinkProperties();
+ mNetlinkSocketObserver.clearNetlinkSocket();
}
// TODO: add a public dump() method that can be called during a bug report.
@@ -215,42 +270,15 @@
ipProbeList.addAll(mIpWatchList);
}
for (InetAddress target : ipProbeList) {
- if (!stillRunning()) { break; }
- probeIp(target);
- }
- }
-
- private void probeIp(InetAddress ip) {
- // This currently does not cause neighbor probing if the target |ip|
- // has been confirmed reachable within the past "delay_probe_time"
- // seconds, i.e. within the past 5 seconds.
- //
- // TODO: replace with a transition directly to NUD_PROBE state once
- // kernels are updated to do so correctly.
- if (DBG) { Log.d(TAG, "Probing ip=" + ip.getHostAddress()); }
-
- final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
- 1, ip, StructNdMsg.NUD_DELAY, mInterfaceIndex, null);
- NetlinkSocket nlSocket = null;
-
- try {
- nlSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
- nlSocket.connectToKernel();
- nlSocket.sendMessage(msg, 0, msg.length, 300);
- final NetlinkMessage response = NetlinkMessage.parse(nlSocket.recvMessage(300));
- if (response != null && response instanceof NetlinkErrorMessage) {
- Log.e(TAG, "Error probing ip=" + response.toString());
+ if (!stillRunning()) {
+ break;
}
- } catch (ErrnoException | InterruptedIOException | SocketException e) {
- Log.d(TAG, "Error probing ip=" + ip.getHostAddress(), e);
- }
-
- if (nlSocket != null) {
- nlSocket.close();
+ probeNeighbor(mInterfaceIndex, target);
}
}
+ // TODO: simply the number of objects by making this extend Thread.
private final class NetlinkSocketObserver implements Runnable {
private static final String TAG = "NetlinkSocketObserver";
private NetlinkSocket mSocket;
@@ -292,7 +320,6 @@
if (mSocket != null) {
mSocket.close();
}
- mSocket = null;
}
// TODO: Refactor the main loop to recreate the socket upon recoverable errors.
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 9c3a623..e6fc1ea 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -27,6 +27,7 @@
import com.android.internal.util.Protocol;
import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* A Utility class for handling for communicating between bearer-specific
@@ -51,6 +52,8 @@
private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>();
private volatile long mLastBwRefreshTime = 0;
private static final long BW_REFRESH_MIN_WIN_MS = 500;
+ private boolean mPollLceScheduled = false;
+ private AtomicBoolean mPollLcePending = new AtomicBoolean(false);
private static final int BASE = Protocol.BASE_NETWORK_AGENT;
@@ -207,11 +210,23 @@
break;
}
case CMD_REQUEST_BANDWIDTH_UPDATE: {
+ long currentTimeMs = System.currentTimeMillis();
if (VDBG) {
log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
}
- if (System.currentTimeMillis() > (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
- pollLceData();
+ if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
+ mPollLceScheduled = false;
+ if (mPollLcePending.getAndSet(true) == false) {
+ pollLceData();
+ }
+ } else {
+ // deliver the request at a later time rather than discard it completely.
+ if (!mPollLceScheduled) {
+ long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS -
+ currentTimeMs + 1;
+ mPollLceScheduled = sendEmptyMessageDelayed(
+ CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
+ }
}
break;
}
@@ -260,6 +275,7 @@
* Called by the bearer code when it has new NetworkCapabilities data.
*/
public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) {
+ mPollLcePending.set(false);
mLastBwRefreshTime = System.currentTimeMillis();
queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED,
new NetworkCapabilities(networkCapabilities));
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 7838b47..e61594c 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -115,6 +115,18 @@
}
/**
+ * Completely clears all the {@code NetworkCapabilities} from this builder instance,
+ * removing even the capabilities that are set by default when the object is constructed.
+ *
+ * @return The builder to facilitate chaining.
+ * @hide
+ */
+ public Builder clearCapabilities() {
+ mNetworkCapabilities.clearAll();
+ return this;
+ }
+
+ /**
* Adds the given transport requirement to this builder. These represent
* the set of allowed transports for the request. Only networks using one
* of these transports will satisfy the request. If no particular transports
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index 2c90909..5f5e623 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -35,13 +35,7 @@
*
* Other HTTP stacks will need to obtain the proxy info from
* {@link Proxy#PROXY_CHANGE_ACTION} broadcast as the extra {@link Proxy#EXTRA_PROXY_INFO}.
- *
- * @deprecated Please use {@link java.net.URL#openConnection}, {@link java.net.Proxy} and
- * friends. The Apache HTTP client is no longer maintained and may be removed in a future
- * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- * for further details.
*/
-@Deprecated
public class ProxyInfo implements Parcelable {
private String mHost;
diff --git a/core/java/android/net/netlink/NetlinkErrorMessage.java b/core/java/android/net/netlink/NetlinkErrorMessage.java
index dbc10d6..e275574 100644
--- a/core/java/android/net/netlink/NetlinkErrorMessage.java
+++ b/core/java/android/net/netlink/NetlinkErrorMessage.java
@@ -18,7 +18,6 @@
import android.net.netlink.StructNlMsgHdr;
import android.net.netlink.NetlinkMessage;
-import android.util.Log;
import java.nio.ByteBuffer;
diff --git a/core/java/android/net/netlink/NetlinkMessage.java b/core/java/android/net/netlink/NetlinkMessage.java
index bc04a16..3bf75ca 100644
--- a/core/java/android/net/netlink/NetlinkMessage.java
+++ b/core/java/android/net/netlink/NetlinkMessage.java
@@ -60,7 +60,7 @@
switch (nlmsghdr.nlmsg_type) {
//case NetlinkConstants.NLMSG_NOOP:
case NetlinkConstants.NLMSG_ERROR:
- return (NetlinkMessage) NetlinkErrorMessage.parse(byteBuffer);
+ return (NetlinkMessage) NetlinkErrorMessage.parse(nlmsghdr, byteBuffer);
case NetlinkConstants.NLMSG_DONE:
byteBuffer.position(byteBuffer.position() + payloadLength);
return new NetlinkMessage(nlmsghdr);
diff --git a/core/java/android/net/netlink/RtNetlinkNeighborMessage.java b/core/java/android/net/netlink/RtNetlinkNeighborMessage.java
index b5f5817..02df131 100644
--- a/core/java/android/net/netlink/RtNetlinkNeighborMessage.java
+++ b/core/java/android/net/netlink/RtNetlinkNeighborMessage.java
@@ -16,6 +16,11 @@
package android.net.netlink;
+import static android.net.netlink.StructNlMsgHdr.NLM_F_ACK;
+import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
+import static android.net.netlink.StructNlMsgHdr.NLM_F_REPLACE;
+import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+
import android.net.netlink.StructNdaCacheInfo;
import android.net.netlink.StructNdMsg;
import android.net.netlink.StructNlAttr;
@@ -123,7 +128,7 @@
final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
nlmsghdr.nlmsg_len = length;
nlmsghdr.nlmsg_type = NetlinkConstants.RTM_GETNEIGH;
- nlmsghdr.nlmsg_flags = StructNlMsgHdr.NLM_F_REQUEST|StructNlMsgHdr.NLM_F_DUMP;
+ nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
nlmsghdr.nlmsg_seq = seqNo;
nlmsghdr.pack(byteBuffer);
@@ -141,7 +146,7 @@
int seqNo, InetAddress ip, short nudState, int ifIndex, byte[] llAddr) {
final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
nlmsghdr.nlmsg_type = NetlinkConstants.RTM_NEWNEIGH;
- nlmsghdr.nlmsg_flags = StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_REPLACE;
+ nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE;
nlmsghdr.nlmsg_seq = seqNo;
final RtNetlinkNeighborMessage msg = new RtNetlinkNeighborMessage(nlmsghdr);
diff --git a/core/java/android/net/netlink/StructNlMsgErr.java b/core/java/android/net/netlink/StructNlMsgErr.java
index 6b02650..f095af4 100644
--- a/core/java/android/net/netlink/StructNlMsgErr.java
+++ b/core/java/android/net/netlink/StructNlMsgErr.java
@@ -52,11 +52,6 @@
public int error;
public StructNlMsgHdr msg;
- public StructNlMsgErr() {
- error = 0;
- msg = null;
- }
-
public void pack(ByteBuffer byteBuffer) {
// The ByteOrder must have already been set by the caller. In most
// cases ByteOrder.nativeOrder() is correct, with the possible
diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java
index 9623695..04e54aa 100644
--- a/core/java/android/os/storage/DiskInfo.java
+++ b/core/java/android/os/storage/DiskInfo.java
@@ -50,6 +50,8 @@
public final int flags;
public long size;
public String label;
+ /** Hacky; don't rely on this count */
+ public int volumeCount;
public DiskInfo(String id, int flags) {
this.id = Preconditions.checkNotNull(id);
@@ -61,6 +63,7 @@
flags = parcel.readInt();
size = parcel.readLong();
label = parcel.readString();
+ volumeCount = parcel.readInt();
}
public @NonNull String getId() {
@@ -181,5 +184,6 @@
parcel.writeInt(this.flags);
parcel.writeLong(size);
parcel.writeString(label);
+ parcel.writeInt(volumeCount);
}
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index e07e846..76a5f967 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -230,6 +230,8 @@
* }
* </pre>
* </p>
+ *
+ * @hide
*/
public static final class Authorization {
/**
@@ -4067,6 +4069,21 @@
public static final String SYNC3 = "data_sync3";
/** Generic column for use by sync adapters. */
public static final String SYNC4 = "data_sync4";
+
+ /**
+ * Carrier presence information.
+ * <P>
+ * Type: INTEGER (A bitmask of CARRIER_PRESENCE_* fields)
+ * </P>
+ */
+ public static final String CARRIER_PRESENCE = "carrier_presence";
+
+ /**
+ * Bitmask flags for CARRIER_PRESENCE column. Each value represents
+ * a bit (or a set of bits) which may be set independently of each
+ * other.
+ */
+ public static final int CARRIER_PRESENCE_VT_CAPABLE = 0x01;
}
/**
@@ -7913,6 +7930,8 @@
/**
* API for inquiring about the general status of the provider.
+ *
+ * @hide
*/
public static final class ProviderStatus {
@@ -8261,7 +8280,7 @@
/**
* Constructs a QuickContacts intent based on an incoming intent for DevicePolicyManager
* to strip off anything not necessary.
- *
+ *
* @hide
*/
public static Intent rebuildManagedQuickContactsIntent(String lookupKey, long contactId,
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index d70712a..cdc196e 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -30,21 +30,21 @@
* <p>A TextureView can be used to display a content stream. Such a content
* stream can for instance be a video or an OpenGL scene. The content stream
* can come from the application's process as well as a remote process.</p>
- *
+ *
* <p>TextureView can only be used in a hardware accelerated window. When
* rendered in software, TextureView will draw nothing.</p>
- *
+ *
* <p>Unlike {@link SurfaceView}, TextureView does not create a separate
* window but behaves as a regular View. This key difference allows a
* TextureView to be moved, transformed, animated, etc. For instance, you
* can make a TextureView semi-translucent by calling
* <code>myView.setAlpha(0.5f)</code>.</p>
- *
+ *
* <p>Using a TextureView is simple: all you need to do is get its
* {@link SurfaceTexture}. The {@link SurfaceTexture} can then be used to
- * render content. The following example demonstrates how to render the
+ * render content. The following example demonstrates how to render the
* camera preview into a TextureView:</p>
- *
+ *
* <pre>
* public class LiveCameraActivity extends Activity implements TextureView.SurfaceTextureListener {
* private Camera mCamera;
@@ -85,19 +85,19 @@
* }
* }
* </pre>
- *
+ *
* <p>A TextureView's SurfaceTexture can be obtained either by invoking
* {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}.
* It is important to know that a SurfaceTexture is available only after the
* TextureView is attached to a window (and {@link #onAttachedToWindow()} has
* been invoked.) It is therefore highly recommended you use a listener to
* be notified when the SurfaceTexture becomes available.</p>
- *
+ *
* <p>It is important to note that only one producer can use the TextureView.
* For instance, if you use a TextureView to display the camera preview, you
* cannot use {@link #lockCanvas()} to draw onto the TextureView at the same
* time.</p>
- *
+ *
* @see SurfaceView
* @see SurfaceTexture
*/
@@ -127,7 +127,7 @@
/**
* Creates a new TextureView.
- *
+ *
* @param context The context to associate this view with.
*/
public TextureView(Context context) {
@@ -137,7 +137,7 @@
/**
* Creates a new TextureView.
- *
+ *
* @param context The context to associate this view with.
* @param attrs The attributes of the XML tag that is inflating the view.
*/
@@ -148,7 +148,7 @@
/**
* Creates a new TextureView.
- *
+ *
* @param context The context to associate this view with.
* @param attrs The attributes of the XML tag that is inflating the view.
* @param defStyleAttr An attribute in the current theme that contains a
@@ -193,7 +193,7 @@
/**
* Indicates whether the content of this TextureView is opaque. The
* content is assumed to be opaque by default.
- *
+ *
* @param opaque True if the content of this TextureView is opaque,
* false otherwise
*/
@@ -258,7 +258,7 @@
* considered to act as a hardware layer. The optional paint supplied to this
* method will however be taken into account when rendering the content of
* this TextureView.
- *
+ *
* @param layerType The ype of layer to use with this view, must be one of
* {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
* {@link #LAYER_TYPE_HARDWARE}
@@ -297,7 +297,7 @@
/**
* Subclasses of TextureView cannot do their own rendering
* with the {@link Canvas} object.
- *
+ *
* @param canvas The Canvas to which the View is rendered.
*/
@Override
@@ -312,7 +312,7 @@
/**
* Subclasses of TextureView cannot do their own rendering
* with the {@link Canvas} object.
- *
+ *
* @param canvas The Canvas to which the View is rendered.
*/
@Override
@@ -435,7 +435,7 @@
return;
}
}
-
+
mLayer.prepare(getWidth(), getHeight(), mOpaque);
mLayer.updateSurfaceTexture();
@@ -449,17 +449,17 @@
* The specified transform applies to the underlying surface
* texture and does not affect the size or position of the view
* itself, only of its content.</p>
- *
+ *
* <p>Some transforms might prevent the content from drawing
* all the pixels contained within this view's bounds. In such
* situations, make sure this texture view is not marked opaque.</p>
- *
+ *
* @param transform The transform to apply to the content of
* this view.
- *
- * @see #getTransform(android.graphics.Matrix)
- * @see #isOpaque()
- * @see #setOpaque(boolean)
+ *
+ * @see #getTransform(android.graphics.Matrix)
+ * @see #isOpaque()
+ * @see #setOpaque(boolean)
*/
public void setTransform(Matrix transform) {
mMatrix.set(transform);
@@ -469,14 +469,14 @@
/**
* Returns the transform associated with this texture view.
- *
+ *
* @param transform The {@link Matrix} in which to copy the current
* transform. Can be null.
- *
+ *
* @return The specified matrix if not null or a new {@link Matrix}
* instance otherwise.
- *
- * @see #setTransform(android.graphics.Matrix)
+ *
+ * @see #setTransform(android.graphics.Matrix)
*/
public Matrix getTransform(Matrix transform) {
if (transform == null) {
@@ -499,21 +499,21 @@
* <p>Returns a {@link android.graphics.Bitmap} representation of the content
* of the associated surface texture. If the surface texture is not available,
* this method returns null.</p>
- *
+ *
* <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
* pixel format and its dimensions are the same as this view's.</p>
- *
+ *
* <p><strong>Do not</strong> invoke this method from a drawing method
* ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
- *
+ *
* <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
- *
+ *
* @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
* texture is not available or the width <= 0 or the height <= 0
- *
- * @see #isAvailable()
- * @see #getBitmap(android.graphics.Bitmap)
- * @see #getBitmap(int, int)
+ *
+ * @see #isAvailable()
+ * @see #getBitmap(android.graphics.Bitmap)
+ * @see #getBitmap(int, int)
*/
public Bitmap getBitmap() {
return getBitmap(getWidth(), getHeight());
@@ -523,24 +523,24 @@
* <p>Returns a {@link android.graphics.Bitmap} representation of the content
* of the associated surface texture. If the surface texture is not available,
* this method returns null.</p>
- *
+ *
* <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
* pixel format.</p>
- *
+ *
* <p><strong>Do not</strong> invoke this method from a drawing method
* ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
- *
+ *
* <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
- *
+ *
* @param width The width of the bitmap to create
* @param height The height of the bitmap to create
- *
+ *
* @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
* texture is not available or width is <= 0 or height is <= 0
- *
- * @see #isAvailable()
- * @see #getBitmap(android.graphics.Bitmap)
- * @see #getBitmap()
+ *
+ * @see #isAvailable()
+ * @see #getBitmap(android.graphics.Bitmap)
+ * @see #getBitmap()
*/
public Bitmap getBitmap(int width, int height) {
if (isAvailable() && width > 0 && height > 0) {
@@ -555,21 +555,21 @@
* bitmap. If the surface texture is not available, the copy is not executed.
* The content of the surface texture will be scaled to fit exactly inside
* the specified bitmap.</p>
- *
+ *
* <p><strong>Do not</strong> invoke this method from a drawing method
* ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
- *
+ *
* <p>If an error occurs, the bitmap is left unchanged.</p>
- *
+ *
* @param bitmap The bitmap to copy the content of the surface texture into,
* cannot be null, all configurations are supported
- *
+ *
* @return The bitmap specified as a parameter
- *
- * @see #isAvailable()
- * @see #getBitmap(int, int)
- * @see #getBitmap()
- *
+ *
+ * @see #isAvailable()
+ * @see #getBitmap(int, int)
+ * @see #getBitmap()
+ *
* @throws IllegalStateException if the hardware rendering context cannot be
* acquired to capture the bitmap
*/
@@ -609,21 +609,21 @@
* to implement
* {@link SurfaceTextureListener#onSurfaceTextureAvailable(android.graphics.SurfaceTexture, int, int)}
* to find out when the Surface is available for use.</p>
- *
+ *
* <p>The content of the Surface is never preserved between unlockCanvas()
* and lockCanvas(), for this reason, every pixel within the Surface area
* must be written. The only exception to this rule is when a dirty
* rectangle is specified, in which case, non-dirty pixels will be
* preserved.</p>
- *
+ *
* <p>This method can only be used if the underlying surface is not already
* owned by another producer. For instance, if the TextureView is being used
* to render the camera's preview you cannot invoke this method.</p>
- *
+ *
* @return A Canvas used to draw into the surface.
- *
- * @see #lockCanvas(android.graphics.Rect)
- * @see #unlockCanvasAndPost(android.graphics.Canvas)
+ *
+ * @see #lockCanvas(android.graphics.Rect)
+ * @see #unlockCanvasAndPost(android.graphics.Canvas)
*/
public Canvas lockCanvas() {
return lockCanvas(null);
@@ -639,12 +639,12 @@
* available (see {@link #isAvailable()} or if the surface texture is
* already connected to an image producer (for instance: the camera,
* OpenGL, a media player, etc.)
- *
+ *
* @param dirty Area of the surface that will be modified.
* @return A Canvas used to draw into the surface.
- *
- * @see #lockCanvas()
+ *
+ * @see #lockCanvas()
* @see #unlockCanvasAndPost(android.graphics.Canvas)
* @see #isAvailable()
*/
@@ -670,11 +670,11 @@
* current pixels will be shown on the screen, but its content is lost,
* in particular there is no guarantee that the content of the Surface
* will remain unchanged when lockCanvas() is called again.
- *
+ *
* @param canvas The Canvas previously returned by lockCanvas()
- *
+ *
* @see #lockCanvas()
- * @see #lockCanvas(android.graphics.Rect)
+ * @see #lockCanvas(android.graphics.Rect)
*/
public void unlockCanvasAndPost(Canvas canvas) {
if (mCanvas != null && canvas == mCanvas) {
@@ -691,8 +691,8 @@
* Returns the {@link SurfaceTexture} used by this view. This method
* may return null if the view is not attached to a window or if the surface
* texture has not been initialized yet.
- *
- * @see #isAvailable()
+ *
+ * @see #isAvailable()
*/
public SurfaceTexture getSurfaceTexture() {
return mSurface;
@@ -730,9 +730,13 @@
}
mSurface = surfaceTexture;
- // If the view is visible, update the listener in the new surface to use
- // the existing listener in the view.
- if (((mViewFlags & VISIBILITY_MASK) == VISIBLE)) {
+ /*
+ * If the view is visible and we already made a layer, update the
+ * listener in the new surface to use the existing listener in the view.
+ * Otherwise this will be called when the view becomes visible or the
+ * layer is created
+ */
+ if (((mViewFlags & VISIBILITY_MASK) == VISIBLE) && mLayer != null) {
mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
}
mUpdateSurface = true;
@@ -742,8 +746,8 @@
/**
* Returns the {@link SurfaceTextureListener} currently associated with this
* texture view.
- *
- * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener)
+ *
+ * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener)
* @see SurfaceTextureListener
*/
public SurfaceTextureListener getSurfaceTextureListener() {
@@ -753,8 +757,8 @@
/**
* Sets the {@link SurfaceTextureListener} used to listen to surface
* texture events.
- *
- * @see #getSurfaceTextureListener()
+ *
+ * @see #getSurfaceTextureListener()
* @see SurfaceTextureListener
*/
public void setSurfaceTextureListener(SurfaceTextureListener listener) {
@@ -777,7 +781,7 @@
public static interface SurfaceTextureListener {
/**
* Invoked when a {@link TextureView}'s SurfaceTexture is ready for use.
- *
+ *
* @param surface The surface returned by
* {@link android.view.TextureView#getSurfaceTexture()}
* @param width The width of the surface
@@ -787,7 +791,7 @@
/**
* Invoked when the {@link SurfaceTexture}'s buffers size changed.
- *
+ *
* @param surface The surface returned by
* {@link android.view.TextureView#getSurfaceTexture()}
* @param width The new width of the surface
@@ -800,7 +804,7 @@
* If returns true, no rendering should happen inside the surface texture after this method
* is invoked. If returns false, the client needs to call {@link SurfaceTexture#release()}.
* Most applications should return true.
- *
+ *
* @param surface The surface about to be destroyed
*/
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface);
@@ -808,7 +812,7 @@
/**
* Invoked when the specified {@link SurfaceTexture} is updated through
* {@link SurfaceTexture#updateTexImage()}.
- *
+ *
* @param surface The surface just updated
*/
public void onSurfaceTextureUpdated(SurfaceTexture surface);
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 7f243d3..e044f1e 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -148,7 +148,6 @@
mInitialized = true;
updateEnabledState(surface);
boolean status = nInitialize(mNativeProxy, surface);
- surface.allocateBuffers();
return status;
}
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index bd45007..f18b7ac 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -80,12 +80,18 @@
/**
* The interpolator of the underlying Animator object. By default, we don't set the interpolator
- * on the Animator and just use its default interpolator. If the interpolator is set to a
- * non-null value on this Animator, then we use the interpolator that it was set to.
+ * on the Animator and just use its default interpolator. If the interpolator is ever set on
+ * this Animator, then we use the interpolator that it was set to.
*/
private TimeInterpolator mInterpolator;
/**
+ * A flag indicating whether the interpolator has been set on this object. If not, we don't set
+ * the interpolator on the underlying Animator, but instead just use its default interpolator.
+ */
+ private boolean mInterpolatorSet = false;
+
+ /**
* Listener for the lifecycle events of the underlying ValueAnimator object.
*/
private Animator.AnimatorListener mListener = null;
@@ -332,6 +338,7 @@
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator setInterpolator(TimeInterpolator interpolator) {
+ mInterpolatorSet = true;
mInterpolator = interpolator;
return this;
}
@@ -342,7 +349,7 @@
* @return The timing interpolator for this animation.
*/
public TimeInterpolator getInterpolator() {
- if (mInterpolator != null) {
+ if (mInterpolatorSet) {
return mInterpolator;
} else {
// Just return the default from ValueAnimator, since that's what we'd get if
@@ -890,7 +897,7 @@
if (mDurationSet) {
animator.setDuration(mDuration);
}
- if (mInterpolator != null) {
+ if (mInterpolatorSet) {
animator.setInterpolator(mInterpolator);
}
animator.start();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c4f9209..41f906a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1705,10 +1705,19 @@
mFullRedrawNeeded = true;
mPreviousTransparentRegion.setEmpty();
+ // Only initialize up-front if transparent regions are not
+ // requested, otherwise defer to see if the entire window
+ // will be transparent
if (mAttachInfo.mHardwareRenderer != null) {
try {
hwInitialized = mAttachInfo.mHardwareRenderer.initialize(
mSurface);
+ if (hwInitialized && (host.mPrivateFlags
+ & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {
+ // Don't pre-allocate if transparent regions
+ // are requested as they may not be needed
+ mSurface.allocateBuffers();
+ }
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
return;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 7976ca4..f0c86e5 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -218,7 +218,7 @@
@ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR, to = "TYPE_NAVIGATION_BAR"),
@ViewDebug.IntToString(from = TYPE_VOLUME_OVERLAY, to = "TYPE_VOLUME_OVERLAY"),
@ViewDebug.IntToString(from = TYPE_BOOT_PROGRESS, to = "TYPE_BOOT_PROGRESS"),
- @ViewDebug.IntToString(from = TYPE_HIDDEN_NAV_CONSUMER, to = "TYPE_HIDDEN_NAV_CONSUMER"),
+ @ViewDebug.IntToString(from = TYPE_INPUT_CONSUMER, to = "TYPE_INPUT_CONSUMER"),
@ViewDebug.IntToString(from = TYPE_DREAM, to = "TYPE_DREAM"),
@ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR_PANEL, to = "TYPE_NAVIGATION_BAR_PANEL"),
@ViewDebug.IntToString(from = TYPE_DISPLAY_OVERLAY, to = "TYPE_DISPLAY_OVERLAY"),
@@ -490,12 +490,11 @@
public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;
/**
- * Window type: Fake window to consume touch events when the navigation
- * bar is hidden.
+ * Window type to consume input events when the systemUI bars are hidden.
* In multiuser systems shows on all users' windows.
* @hide
*/
- public static final int TYPE_HIDDEN_NAV_CONSUMER = FIRST_SYSTEM_WINDOW+22;
+ public static final int TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22;
/**
* Window type: Dreams (screen saver) window, just above keyguard.
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 9199af1..1b759a3 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -373,12 +373,12 @@
}
/**
- * Representation of a "fake window" that the policy has added to the
- * window manager to consume events.
+ * Representation of a input consumer that the policy has added to the
+ * window manager to consume input events going to windows below it.
*/
- public interface FakeWindow {
+ public interface InputConsumer {
/**
- * Remove the fake window from the window manager.
+ * Remove the input consumer from the window manager.
*/
void dismiss();
}
@@ -402,13 +402,10 @@
public void reevaluateStatusBarVisibility();
/**
- * Add a fake window to the window manager. This window sits
- * at the top of the other windows and consumes events.
+ * Add a input consumer which will consume all input events going to any window below it.
*/
- public FakeWindow addFakeWindow(Looper looper,
- InputEventReceiver.Factory inputEventReceiverFactory,
- String name, int windowType, int layoutParamsFlags, int layoutParamsPrivateFlags,
- boolean canReceiveKeys, boolean hasFocus, boolean touchFullscreen);
+ public InputConsumer addInputConsumer(Looper looper,
+ InputEventReceiver.Factory inputEventReceiverFactory);
/**
* Returns a code that describes the current state of the lid switch.
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index d558c7b..19184d6 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1699,6 +1699,15 @@
* @return true if the selection mode was actually started.
*/
private boolean startSelectionActionModeWithoutSelection() {
+ if (extractedTextModeWillBeStarted()) {
+ // Cancel the single tap delayed runnable.
+ if (mSelectionModeWithoutSelectionRunnable != null) {
+ mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable);
+ }
+
+ return false;
+ }
+
if (mSelectionActionMode != null) {
// Selection action mode is already started
// TODO: revisit invocations to minimize this case.
@@ -1740,6 +1749,15 @@
}
private boolean startSelectionActionModeWithSelectionInternal() {
+ if (extractedTextModeWillBeStarted()) {
+ // Cancel the single tap delayed runnable.
+ if (mSelectionModeWithoutSelectionRunnable != null) {
+ mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable);
+ }
+
+ return false;
+ }
+
if (mSelectionActionMode != null) {
// Selection action mode is already started
mSelectionActionMode.invalidate();
@@ -3477,6 +3495,10 @@
mIdealVerticalOffset = 0.7f * handleHeight;
}
+ public float getIdealVerticalOffset() {
+ return mIdealVerticalOffset;
+ }
+
protected void updateDrawable() {
final int offset = getCurrentCursorOffset();
final boolean isRtlCharAtOffset = mTextView.getLayout().isRtlCharAt(offset);
@@ -3948,7 +3970,11 @@
@Override
protected int getHotspotX(Drawable drawable, boolean isRtlRun) {
- return isRtlRun ? 0 : drawable.getIntrinsicWidth();
+ if (isRtlRun) {
+ return drawable.getIntrinsicWidth() / 4;
+ } else {
+ return (drawable.getIntrinsicWidth() * 3) / 4;
+ }
}
@Override
@@ -4066,7 +4092,11 @@
@Override
protected int getHotspotX(Drawable drawable, boolean isRtlRun) {
- return isRtlRun ? drawable.getIntrinsicWidth() : 0;
+ if (isRtlRun) {
+ return (drawable.getIntrinsicWidth() * 3) / 4;
+ } else {
+ return drawable.getIntrinsicWidth() / 4;
+ }
}
@Override
@@ -4253,6 +4283,7 @@
private int mStartOffset = -1;
// Indicates whether the user is selecting text and using the drag accelerator.
private boolean mDragAcceleratorActive;
+ private boolean mHaventMovedEnoughToStartDrag;
SelectionModifierCursorController() {
resetTouchOffsets();
@@ -4317,19 +4348,20 @@
public void onTouchEvent(MotionEvent event) {
// This is done even when the View does not have focus, so that long presses can start
// selection and tap can move cursor from this tap position.
+ final float eventX = event.getX();
+ final float eventY = event.getY();
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- final float x = event.getX();
- final float y = event.getY();
// Remember finger down position, to be able to start selection from there.
- mMinTouchOffset = mMaxTouchOffset = mTextView.getOffsetForPosition(x, y);
+ mMinTouchOffset = mMaxTouchOffset = mTextView.getOffsetForPosition(
+ eventX, eventY);
// Double tap detection
if (mGestureStayedInTapRegion) {
if (mDoubleTap) {
- final float deltaX = x - mDownPositionX;
- final float deltaY = y - mDownPositionY;
+ final float deltaX = eventX - mDownPositionX;
+ final float deltaY = eventY - mDownPositionY;
final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
ViewConfiguration viewConfiguration = ViewConfiguration.get(
@@ -4337,16 +4369,17 @@
int doubleTapSlop = viewConfiguration.getScaledDoubleTapSlop();
boolean stayedInArea = distanceSquared < doubleTapSlop * doubleTapSlop;
- if (stayedInArea && isPositionOnText(x, y)) {
+ if (stayedInArea && isPositionOnText(eventX, eventY)) {
startSelectionActionModeWithSelectionAndStartDrag();
mDiscardNextActionUp = true;
}
}
}
- mDownPositionX = x;
- mDownPositionY = y;
+ mDownPositionX = eventX;
+ mDownPositionY = eventY;
mGestureStayedInTapRegion = true;
+ mHaventMovedEnoughToStartDrag = true;
break;
case MotionEvent.ACTION_POINTER_DOWN:
@@ -4360,18 +4393,24 @@
break;
case MotionEvent.ACTION_MOVE:
- final ViewConfiguration viewConfiguration = ViewConfiguration.get(
+ final ViewConfiguration viewConfig = ViewConfiguration.get(
mTextView.getContext());
+ final int touchSlop = viewConfig.getScaledTouchSlop();
- if (mGestureStayedInTapRegion) {
- final float deltaX = event.getX() - mDownPositionX;
- final float deltaY = event.getY() - mDownPositionY;
+ if (mGestureStayedInTapRegion || mHaventMovedEnoughToStartDrag) {
+ final float deltaX = eventX - mDownPositionX;
+ final float deltaY = eventY - mDownPositionY;
final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
- int doubleTapTouchSlop = viewConfiguration.getScaledDoubleTapTouchSlop();
-
- if (distanceSquared > doubleTapTouchSlop * doubleTapTouchSlop) {
- mGestureStayedInTapRegion = false;
+ if (mGestureStayedInTapRegion) {
+ int doubleTapTouchSlop = viewConfig.getScaledDoubleTapTouchSlop();
+ mGestureStayedInTapRegion =
+ distanceSquared <= doubleTapTouchSlop * doubleTapTouchSlop;
+ }
+ if (mHaventMovedEnoughToStartDrag) {
+ // We don't start dragging until the user has moved enough.
+ mHaventMovedEnoughToStartDrag =
+ distanceSquared <= touchSlop * touchSlop;
}
}
@@ -4381,56 +4420,28 @@
}
if (mStartOffset != -1) {
- final int rawOffset = mTextView.getOffsetForPosition(event.getX(),
- event.getY());
- int offset = rawOffset;
-
- // We don't start "dragging" until the user is past the initial word that
- // gets selected on long press.
- int firstWordStart = getWordStart(mStartOffset);
- int firstWordEnd = getWordEnd(mStartOffset);
- if (offset > firstWordEnd || offset < firstWordStart) {
-
- // Basically the goal in the below code is to have the highlight be
- // offset so that your finger isn't covering the end point.
- int fingerOffset = viewConfiguration.getScaledTouchSlop();
- float mx = event.getX();
- float my = event.getY();
- if (mx > fingerOffset) mx -= fingerOffset;
- if (my > fingerOffset) my -= fingerOffset;
- offset = mTextView.getOffsetForPosition(mx, my);
-
- // Perform the check for closeness at edge of view, if we're very close
- // don't adjust the offset to be in front of the finger - otherwise the
- // user can't select words at the edge.
- if (mTextView.getWidth() - fingerOffset > mx) {
- // We're going by word, so we need to make sure that the offset
- // that we get is within this, so we'll get the previous boundary.
- final WordIterator wordIterator = getWordIteratorWithText();
-
- final int precedingOffset = wordIterator.preceding(offset);
- if (mStartOffset < offset) {
- // Expanding with bottom handle, in this case the selection end
- // is before the finger.
- offset = Math.max(precedingOffset - 1, 0);
- } else {
- // Expand with the start handle, in this case the selection
- // start is before the finger.
- if (precedingOffset == WordIterator.DONE) {
- offset = 0;
- } else {
- offset = wordIterator.preceding(precedingOffset);
- }
- }
+ if (!mHaventMovedEnoughToStartDrag) {
+ // Offset the finger by the same vertical offset as the handles. This
+ // improves visibility of the content being selected by shifting
+ // the finger below the content.
+ final float fingerOffset = (mStartHandle != null)
+ ? mStartHandle.getIdealVerticalOffset()
+ : touchSlop;
+ int offset =
+ mTextView.getOffsetForPosition(eventX, eventY - fingerOffset);
+ int startOffset;
+ // Snap to word boundaries.
+ if (mStartOffset < offset) {
+ // Expanding with end handle.
+ offset = getWordEnd(offset);
+ startOffset = getWordStart(mStartOffset);
+ } else {
+ // Expanding with start handle.
+ offset = getWordStart(offset);
+ startOffset = getWordEnd(mStartOffset);
}
- if (offset == WordIterator.DONE)
- offset = rawOffset;
-
- // Need to adjust start offset based on direction of movement.
- int newStart = mStartOffset < offset ? getWordStart(mStartOffset)
- : getWordEnd(mStartOffset);
- Selection.setSelection((Spannable) mTextView.getText(), newStart,
- offset);
+ Selection.setSelection((Spannable) mTextView.getText(),
+ startOffset, offset);
}
}
break;
diff --git a/core/java/com/android/internal/logging/MetricsConstants.java b/core/java/com/android/internal/logging/MetricsConstants.java
index b9a75d3..65dc743 100644
--- a/core/java/com/android/internal/logging/MetricsConstants.java
+++ b/core/java/com/android/internal/logging/MetricsConstants.java
@@ -208,7 +208,6 @@
public static final int APPLICATIONS_USAGE_ACCESS_DETAIL = 183;
public static final int APPLICATIONS_HIGH_POWER_APPS = 184;
public static final int FUELGAUGE_HIGH_POWER_DETAILS = 185;
- public static final int APPLICATIONS_MANAGE_ASSIST = 186;
//aliases
public static final int DEVICEINFO_STORAGE = DEVICEINFO_MEMORY;
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 32b5b02..5144457 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -27,6 +27,10 @@
#define ENCODING_E_AC3 6
#define ENCODING_DTS 7
#define ENCODING_DTS_HD 8
+#define ENCODING_MP3 9
+#define ENCODING_AAC_LC 10
+#define ENCODING_AAC_HE_V1 11
+#define ENCODING_AAC_HE_V2 12
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -52,6 +56,14 @@
return AUDIO_FORMAT_DTS;
case ENCODING_DTS_HD:
return AUDIO_FORMAT_DTS_HD;
+ case ENCODING_MP3:
+ return AUDIO_FORMAT_MP3;
+ case ENCODING_AAC_LC:
+ return AUDIO_FORMAT_AAC_LC;
+ case ENCODING_AAC_HE_V1:
+ return AUDIO_FORMAT_AAC_HE_V1;
+ case ENCODING_AAC_HE_V2:
+ return AUDIO_FORMAT_AAC_HE_V2;
case ENCODING_DEFAULT:
return AUDIO_FORMAT_DEFAULT;
default:
@@ -76,6 +88,14 @@
return ENCODING_DTS;
case AUDIO_FORMAT_DTS_HD:
return ENCODING_DTS_HD;
+ case AUDIO_FORMAT_MP3:
+ return ENCODING_MP3;
+ case AUDIO_FORMAT_AAC_LC:
+ return ENCODING_AAC_LC;
+ case AUDIO_FORMAT_AAC_HE_V1:
+ return ENCODING_AAC_HE_V1;
+ case AUDIO_FORMAT_AAC_HE_V2:
+ return ENCODING_AAC_HE_V2;
case AUDIO_FORMAT_DEFAULT:
return ENCODING_DEFAULT;
default:
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 87b81d5..e5c4ba9 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -521,11 +521,11 @@
}
// ----------------------------------------------------------------------------
-static jint android_media_AudioRecord_get_native_frame_count(JNIEnv *env, jobject thiz) {
+static jint android_media_AudioRecord_get_buffer_size_in_frames(JNIEnv *env, jobject thiz) {
sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
if (lpRecorder == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve AudioRecord pointer for getNativeFrameCount()");
+ "Unable to retrieve AudioRecord pointer for frameCount()");
return (jint)AUDIO_JAVA_ERROR;
}
return lpRecorder->frameCount();
@@ -700,8 +700,8 @@
(void *)android_media_AudioRecord_readInArray<jfloatArray>},
{"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I",
(void *)android_media_AudioRecord_readInDirectBuffer},
- {"native_get_native_frame_count",
- "()I", (void *)android_media_AudioRecord_get_native_frame_count},
+ {"native_get_buffer_size_in_frames",
+ "()I", (void *)android_media_AudioRecord_get_buffer_size_in_frames},
{"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos},
{"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos},
{"native_set_pos_update_period",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 595f9f0..4f451c7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -681,7 +681,7 @@
android:permissionGroup="android.permission-group.SENSORS"
android:label="@string/permlab_useFingerprint"
android:description="@string/permdesc_useFingerprint"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="normal" />
<!-- ====================================================================== -->
<!-- INSTALLTIME PERMISSIONS -->
diff --git a/core/res/res/drawable-hdpi/text_select_handle_left_mtrl_alpha.png b/core/res/res/drawable-hdpi/text_select_handle_left_mtrl_alpha.png
index 1550b44..7ccb70a 100644
--- a/core/res/res/drawable-hdpi/text_select_handle_left_mtrl_alpha.png
+++ b/core/res/res/drawable-hdpi/text_select_handle_left_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_select_handle_right_mtrl_alpha.png b/core/res/res/drawable-hdpi/text_select_handle_right_mtrl_alpha.png
index b309dfd..e65b89d 100644
--- a/core/res/res/drawable-hdpi/text_select_handle_right_mtrl_alpha.png
+++ b/core/res/res/drawable-hdpi/text_select_handle_right_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_left_mtrl_alpha.png b/core/res/res/drawable-mdpi/text_select_handle_left_mtrl_alpha.png
index b36a413..775f1bb 100644
--- a/core/res/res/drawable-mdpi/text_select_handle_left_mtrl_alpha.png
+++ b/core/res/res/drawable-mdpi/text_select_handle_left_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_right_mtrl_alpha.png b/core/res/res/drawable-mdpi/text_select_handle_right_mtrl_alpha.png
index afd0bd2..68fd053 100644
--- a/core/res/res/drawable-mdpi/text_select_handle_right_mtrl_alpha.png
+++ b/core/res/res/drawable-mdpi/text_select_handle_right_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/text_select_handle_left_mtrl_alpha.png b/core/res/res/drawable-xhdpi/text_select_handle_left_mtrl_alpha.png
index 58f8c43..b5c2a91 100644
--- a/core/res/res/drawable-xhdpi/text_select_handle_left_mtrl_alpha.png
+++ b/core/res/res/drawable-xhdpi/text_select_handle_left_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/text_select_handle_right_mtrl_alpha.png b/core/res/res/drawable-xhdpi/text_select_handle_right_mtrl_alpha.png
index 42a893d..6c6185c 100644
--- a/core/res/res/drawable-xhdpi/text_select_handle_right_mtrl_alpha.png
+++ b/core/res/res/drawable-xhdpi/text_select_handle_right_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/text_select_handle_left_mtrl_alpha.png b/core/res/res/drawable-xxhdpi/text_select_handle_left_mtrl_alpha.png
index d0f274a..f0e32af 100644
--- a/core/res/res/drawable-xxhdpi/text_select_handle_left_mtrl_alpha.png
+++ b/core/res/res/drawable-xxhdpi/text_select_handle_left_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/text_select_handle_right_mtrl_alpha.png b/core/res/res/drawable-xxhdpi/text_select_handle_right_mtrl_alpha.png
index f1f637a..260e090 100644
--- a/core/res/res/drawable-xxhdpi/text_select_handle_right_mtrl_alpha.png
+++ b/core/res/res/drawable-xxhdpi/text_select_handle_right_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/text_select_handle_left_mtrl_alpha.png b/core/res/res/drawable-xxxhdpi/text_select_handle_left_mtrl_alpha.png
index 643168f..a7a48b8 100644
--- a/core/res/res/drawable-xxxhdpi/text_select_handle_left_mtrl_alpha.png
+++ b/core/res/res/drawable-xxxhdpi/text_select_handle_left_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/text_select_handle_right_mtrl_alpha.png b/core/res/res/drawable-xxxhdpi/text_select_handle_right_mtrl_alpha.png
index e8f3aad..2c72f4f 100644
--- a/core/res/res/drawable-xxxhdpi/text_select_handle_right_mtrl_alpha.png
+++ b/core/res/res/drawable-xxxhdpi/text_select_handle_right_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f790d74..c715652 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2142,8 +2142,9 @@
<!-- Keyguard component -->
<string name="config_keyguardComponent" translatable="false">com.android.systemui/com.android.systemui.keyguard.KeyguardService</string>
- <!-- This config is used to force VoiceInteractionService to start on certain low ram devices. -->
- <bool name="config_forceEnableVoiceInteractionService">false</bool>
+ <!-- This config is used to force VoiceInteractionService to start on certain low ram devices.
+ It declares the package name of VoiceInteractionService that should be started. -->
+ <string translatable="false" name="config_forceVoiceInteractionServicePackage"></string>
<!-- This config is ued to determine whether animations are allowed in low power mode. -->
<bool name="config_allowAnimationsInLowPowerMode">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6124b5b..4b57a47 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -280,7 +280,6 @@
<java-symbol type="bool" name="config_enableScreenshotChord" />
<java-symbol type="bool" name="config_bluetooth_default_profiles" />
<java-symbol type="bool" name="config_enableWifiDisplay" />
- <java-symbol type="bool" name="config_forceEnableVoiceInteractionService" />
<java-symbol type="bool" name="config_allowAnimationsInLowPowerMode" />
<java-symbol type="bool" name="config_useDevInputEventForAudioJack" />
<java-symbol type="bool" name="config_safe_media_volume_enabled" />
@@ -560,6 +559,7 @@
<java-symbol type="string" name="chooseActivity" />
<java-symbol type="string" name="config_default_dns_server" />
<java-symbol type="string" name="config_ethernet_iface_regex" />
+ <java-symbol type="string" name="config_forceVoiceInteractionServicePackage" />
<java-symbol type="string" name="config_mms_user_agent" />
<java-symbol type="string" name="config_mms_user_agent_profile_url" />
<java-symbol type="string" name="config_ntpServer" />
diff --git a/core/tests/coretests/src/android/net/netlink/NetlinkErrorMessageTest.java b/core/tests/coretests/src/android/net/netlink/NetlinkErrorMessageTest.java
new file mode 100644
index 0000000..e677475
--- /dev/null
+++ b/core/tests/coretests/src/android/net/netlink/NetlinkErrorMessageTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.netlink;
+
+import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+import static android.net.netlink.StructNlMsgHdr.NLM_F_ACK;
+import static android.net.netlink.StructNlMsgHdr.NLM_F_REPLACE;
+
+import android.net.netlink.NetlinkConstants;
+import android.net.netlink.NetlinkErrorMessage;
+import android.net.netlink.NetlinkMessage;
+import android.net.netlink.StructNlMsgErr;
+import android.util.Log;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import junit.framework.TestCase;
+import libcore.util.HexEncoding;
+
+
+public class NetlinkErrorMessageTest extends TestCase {
+ private final String TAG = "NetlinkErrorMessageTest";
+
+ // Hexadecimal representation of packet capture.
+ public static final String NLM_ERROR_OK_HEX =
+ // struct nlmsghdr
+ "24000000" + // length = 36
+ "0200" + // type = 2 (NLMSG_ERROR)
+ "0000" + // flags
+ "26350000" + // seqno
+ "64100000" + // pid = userspace process
+ // error integer
+ "00000000" + // "errno" (0 == OK)
+ // struct nlmsghdr
+ "30000000" + // length (48) of original request
+ "1C00" + // type = 28 (RTM_NEWNEIGH)
+ "0501" + // flags (NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE)
+ "26350000" + // seqno
+ "00000000"; // pid = kernel
+ public static final byte[] NLM_ERROR_OK =
+ HexEncoding.decode(NLM_ERROR_OK_HEX.toCharArray(), false);
+
+ public void testParseNlmErrorOk() {
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(NLM_ERROR_OK);
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
+ final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer);
+ assertNotNull(msg);
+ assertTrue(msg instanceof NetlinkErrorMessage);
+ final NetlinkErrorMessage errorMsg = (NetlinkErrorMessage) msg;
+
+ final StructNlMsgHdr hdr = errorMsg.getHeader();
+ assertNotNull(hdr);
+ assertEquals(36, hdr.nlmsg_len);
+ assertEquals(NetlinkConstants.NLMSG_ERROR, hdr.nlmsg_type);
+ assertEquals(0, hdr.nlmsg_flags);
+ assertEquals(13606, hdr.nlmsg_seq);
+ assertEquals(4196, hdr.nlmsg_pid);
+
+ final StructNlMsgErr err = errorMsg.getNlMsgError();
+ assertNotNull(err);
+ assertEquals(0, err.error);
+ assertNotNull(err.msg);
+ assertEquals(48, err.msg.nlmsg_len);
+ assertEquals(NetlinkConstants.RTM_NEWNEIGH, err.msg.nlmsg_type);
+ assertEquals((NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE), err.msg.nlmsg_flags);
+ assertEquals(13606, err.msg.nlmsg_seq);
+ assertEquals(0, err.msg.nlmsg_pid);
+ }
+}
diff --git a/core/tests/coretests/src/android/net/netlink/NetlinkSocketTest.java b/core/tests/coretests/src/android/net/netlink/NetlinkSocketTest.java
index b32de78..c599fe3 100644
--- a/core/tests/coretests/src/android/net/netlink/NetlinkSocketTest.java
+++ b/core/tests/coretests/src/android/net/netlink/NetlinkSocketTest.java
@@ -90,4 +90,27 @@
s.close();
}
+
+ public void testRepeatedCloseCallsAreQuiet() throws Exception {
+ // Create a working NetlinkSocket.
+ NetlinkSocket s = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
+ assertNotNull(s);
+ s.connectToKernel();
+ NetlinkSocketAddress localAddr = s.getLocalAddress();
+ assertNotNull(localAddr);
+ assertEquals(0, localAddr.getGroupsMask());
+ assertTrue(0 != localAddr.getPortId());
+ // Close once.
+ s.close();
+ // Test that it is closed.
+ boolean expectedErrorSeen = false;
+ try {
+ localAddr = s.getLocalAddress();
+ } catch (ErrnoException e) {
+ expectedErrorSeen = true;
+ }
+ assertTrue(expectedErrorSeen);
+ // Close once more.
+ s.close();
+ }
}
diff --git a/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java b/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
index a7bebad..19ee000 100644
--- a/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
+++ b/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
@@ -222,7 +222,7 @@
// struct nlmsghdr
"30000000" + // length = 48
"1c00" + // type = 28 (RTM_NEWNEIGH)
- "0101" + // flags (NLM_F_REQUEST | NLM_F_REPLACE)
+ "0501" + // flags (NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE)
"4b0a0000" + // seqno
"00000000" + // pid (0 == kernel)
// struct ndmsg
diff --git a/docs/html/preview/images/bugs.png b/docs/html/preview/images/bugs.png
deleted file mode 100644
index 46adf05..0000000
--- a/docs/html/preview/images/bugs.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/images/dev-prev.png b/docs/html/preview/images/dev-prev.png
deleted file mode 100644
index eae6ede..0000000
--- a/docs/html/preview/images/dev-prev.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/images/m-preview-timeline.png b/docs/html/preview/images/m-preview-timeline.png
index a065c21..bda0b46 100644
--- a/docs/html/preview/images/m-preview-timeline.png
+++ b/docs/html/preview/images/m-preview-timeline.png
Binary files differ
diff --git a/docs/html/preview/images/updates.png b/docs/html/preview/images/updates.png
deleted file mode 100644
index f165c5a..0000000
--- a/docs/html/preview/images/updates.png
+++ /dev/null
Binary files differ
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index a999b71..8ad7c12 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1575,11 +1575,12 @@
*/
public boolean sameAs(Bitmap other) {
checkRecycled("Can't call sameAs on a recycled bitmap!");
+ if (this == other) return true;
+ if (other == null) return false;
if (other.isRecycled()) {
throw new IllegalArgumentException("Can't compare to a recycled bitmap!");
}
- return this == other || (other != null
- && nativeSameAs(mFinalizer.mNativeBitmap, other.mFinalizer.mNativeBitmap));
+ return nativeSameAs(mFinalizer.mNativeBitmap, other.mFinalizer.mNativeBitmap);
}
/**
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index f75d6a0..1030451 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -23,6 +23,7 @@
#include "Properties.h"
#include "renderstate/RenderState.h"
#include "ShadowTessellator.h"
+#include "utils/GLUtils.h"
#include <utils/Log.h>
#include <utils/String8.h>
@@ -276,6 +277,9 @@
clearGarbage();
glFinish();
+ // Errors during cleanup should be considered non-fatal, dump them and
+ // and move on. TODO: All errors or just errors like bad surface?
+ GLUtils::dumpGLErrors();
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 6b8c780..a17904e 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -119,7 +119,6 @@
void DeferredLayerUpdater::detachSurfaceTexture() {
if (mSurfaceTexture.get()) {
- mRenderThread.eglManager().requireGlContext();
status_t err = mSurfaceTexture->detachFromContext();
if (err != 0) {
// TODO: Elevate to fatal exception
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index e54fa5a..d7c8316 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -169,7 +169,8 @@
void RenderState::requireGLContext() {
assertOnGLThread();
- mRenderThread.eglManager().requireGlContext();
+ LOG_ALWAYS_FATAL_IF(!mRenderThread.eglManager().hasEglContext(),
+ "No GL context!");
}
void RenderState::assertOnGLThread() {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 8329cd4..706e14e 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -262,8 +262,6 @@
if (drew) {
swapBuffers(dirty, width, height);
- } else {
- mEglManager.cancelFrame();
}
// TODO: Use a fence for real completion?
@@ -297,7 +295,6 @@
ATRACE_CALL();
DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
if (thread.eglManager().hasEglContext()) {
- thread.eglManager().requireGlContext();
mode = DrawGlInfo::kModeProcess;
}
@@ -318,7 +315,6 @@
void CanvasContext::freePrefetechedLayers() {
if (mPrefetechedLayers.size()) {
- requireGlContext();
std::for_each(mPrefetechedLayers.begin(), mPrefetechedLayers.end(), destroyPrefetechedNode);
mPrefetechedLayers.clear();
}
@@ -329,7 +325,6 @@
if (!mEglManager.hasEglContext() || !mCanvas) {
return;
}
- requireGlContext();
// buildLayer() will leave the tree in an unknown state, so we must stop drawing
stopDrawing();
@@ -352,7 +347,6 @@
}
bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
- requireGlContext();
layer->apply();
return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap);
}
@@ -360,7 +354,6 @@
void CanvasContext::destroyHardwareResources() {
stopDrawing();
if (mEglManager.hasEglContext()) {
- requireGlContext();
freePrefetechedLayers();
mRootRenderNode->destroyHardwareResources();
Caches::getInstance().flush(Caches::kFlushMode_Layers);
@@ -372,7 +365,6 @@
if (!thread.eglManager().hasEglContext()) return;
ATRACE_CALL();
- thread.eglManager().requireGlContext();
if (level >= TRIM_MEMORY_COMPLETE) {
Caches::getInstance().flush(Caches::kFlushMode_Full);
thread.eglManager().destroy();
@@ -382,7 +374,8 @@
}
void CanvasContext::runWithGlContext(RenderTask* task) {
- requireGlContext();
+ LOG_ALWAYS_FATAL_IF(!mEglManager.hasEglContext(),
+ "GL context not initialized!");
task->run();
}
@@ -391,10 +384,6 @@
return LayerRenderer::createTextureLayer(mRenderThread.renderState());
}
-void CanvasContext::requireGlContext() {
- mEglManager.requireGlContext();
-}
-
void CanvasContext::setTextureAtlas(RenderThread& thread,
const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) {
thread.eglManager().setTextureAtlas(buffer, map, mapSize);
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 17917af..10e66e9 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -122,8 +122,6 @@
void swapBuffers(const SkRect& dirty, EGLint width, EGLint height);
void requireSurface();
- void requireGlContext();
-
void freePrefetechedLayers();
RenderThread& mRenderThread;
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 6255f5e..c0e7c73 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -78,8 +78,7 @@
, mAllowPreserveBuffer(load_dirty_regions_property())
, mCurrentSurface(EGL_NO_SURFACE)
, mAtlasMap(nullptr)
- , mAtlasMapSize(0)
- , mInFrame(false) {
+ , mAtlasMapSize(0) {
mCanSetPreserveBuffer = mAllowPreserveBuffer;
ALOGD("Use EGL_SWAP_BEHAVIOR_PRESERVED: %s", mAllowPreserveBuffer ? "true" : "false");
}
@@ -101,7 +100,8 @@
loadConfig();
createContext();
- usePBufferSurface();
+ createPBufferSurface();
+ makeCurrent(mPBufferSurface);
mRenderThread.renderState().onGLContextCreated();
initAtlas();
}
@@ -110,17 +110,6 @@
return mEglDisplay != EGL_NO_DISPLAY;
}
-void EglManager::requireGlContext() {
- LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, "No EGL context");
-
- if (!mInFrame) {
- // We can't be certain about the state of the current surface (whether
- // or not it is destroyed, for example), so err on the side of using
- // the pbuffer surface which we fully control
- usePBufferSurface();
- }
-}
-
void EglManager::loadConfig() {
EGLint swapBehavior = mCanSetPreserveBuffer ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
EGLint attribs[] = {
@@ -173,7 +162,6 @@
mAtlasMapSize = mapSize;
if (hasEglContext()) {
- usePBufferSurface();
initAtlas();
}
}
@@ -185,7 +173,7 @@
}
}
-void EglManager::usePBufferSurface() {
+void EglManager::createPBufferSurface() {
LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
"usePBufferSurface() called on uninitialized GlobalContext!");
@@ -193,7 +181,6 @@
EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
}
- makeCurrent(mPBufferSurface);
}
EGLSurface EglManager::createSurface(EGLNativeWindowType window) {
@@ -217,8 +204,6 @@
void EglManager::destroy() {
if (mEglDisplay == EGL_NO_DISPLAY) return;
- usePBufferSurface();
-
mRenderThread.renderState().onGLContextDestroyed();
eglDestroyContext(mEglDisplay, mEglContext);
eglDestroySurface(mEglDisplay, mPBufferSurface);
@@ -236,11 +221,10 @@
if (isCurrent(surface)) return false;
if (surface == EGL_NO_SURFACE) {
- // If we are setting EGL_NO_SURFACE we don't care about any of the potential
- // return errors, which would only happen if mEglDisplay had already been
- // destroyed in which case the current context is already NO_CONTEXT
- eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
+ // Ensure we always have a valid surface & context
+ surface = mPBufferSurface;
+ }
+ if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
(void*)surface, egl_error_str());
}
@@ -259,12 +243,10 @@
eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
}
eglBeginFrame(mEglDisplay, surface);
- mInFrame = true;
}
bool EglManager::swapBuffers(EGLSurface surface, const SkRect& dirty,
EGLint width, EGLint height) {
- mInFrame = false;
#if WAIT_FOR_GPU_COMPLETION
{
@@ -328,10 +310,6 @@
eglDestroySyncKHR(mEglDisplay, fence);
}
-void EglManager::cancelFrame() {
- mInFrame = false;
-}
-
bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) {
if (CC_UNLIKELY(!mAllowPreserveBuffer)) return false;
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index 0855516..8881de6 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -36,9 +36,7 @@
void initialize();
bool hasEglContext();
- void requireGlContext();
- void usePBufferSurface();
EGLSurface createSurface(EGLNativeWindowType window);
void destroySurface(EGLSurface surface);
@@ -49,7 +47,6 @@
bool makeCurrent(EGLSurface surface);
void beginFrame(EGLSurface surface, EGLint* width, EGLint* height);
bool swapBuffers(EGLSurface surface, const SkRect& dirty, EGLint width, EGLint height);
- void cancelFrame();
// Returns true iff the surface is now preserving buffers.
bool setPreserveBuffer(EGLSurface surface, bool preserve);
@@ -65,6 +62,7 @@
// EglContext is never destroyed, method is purposely not implemented
~EglManager();
+ void createPBufferSurface();
void loadConfig();
void createContext();
void initAtlas();
@@ -84,12 +82,6 @@
sp<GraphicBuffer> mAtlasBuffer;
int64_t* mAtlasMap;
size_t mAtlasMapSize;
-
- // Whether or not we are in the middle of drawing a frame. This is used
- // to avoid switching surfaces mid-frame if requireGlContext() is called
- // TODO: Need to be better about surface/context management so that this isn't
- // necessary
- bool mInFrame;
};
} /* namespace renderthread */
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 4526839..0f1be6b 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -17,6 +17,7 @@
package android.media;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -724,6 +725,26 @@
return USAGE_UNKNOWN;
}
}
+ /**
+ * @hide
+ * CANDIDATE FOR PUBLIC (or at least SYSTEM) API
+ * Returns the stream type matching the given attributes for volume control.
+ * Use this method to derive the stream type needed to configure the volume
+ * control slider in an {@link Activity} with {@link Activity#setVolumeControlStream(int)}.
+ * <BR>Do not use this method to set the stream type on an audio player object
+ * (e.g. {@link AudioTrack}, {@link MediaPlayer}), use <code>AudioAttributes</code> instead.
+ * @param aa non-null AudioAttributes.
+ * @return a valid stream type for <code>Activity</code> or stream volume control that matches
+ * the attributes, or {@link AudioManager#USE_DEFAULT_STREAM_TYPE} if there isn't a direct
+ * match. Note that <code>USE_DEFAULT_STREAM_TYPE</code> is not a valid value
+ * for {@link AudioManager#setStreamVolume(int, int, int)}.
+ */
+ public static int getVolumeControlStream(@NonNull AudioAttributes aa) {
+ if (aa == null) {
+ throw new IllegalArgumentException("Invalid null audio attributes");
+ }
+ return toVolumeStreamType(true /*fromGetVolumeControlStream*/, aa);
+ }
/**
* @hide
@@ -732,13 +753,19 @@
* @param aa non-null AudioAttributes.
* @return a valid stream type for volume control that matches the attributes.
*/
- public static int toLegacyStreamType(AudioAttributes aa) {
+ public static int toLegacyStreamType(@NonNull AudioAttributes aa) {
+ return toVolumeStreamType(false /*fromGetVolumeControlStream*/, aa);
+ }
+
+ private static int toVolumeStreamType(boolean fromGetVolumeControlStream, AudioAttributes aa) {
// flags to stream type mapping
if ((aa.getFlags() & FLAG_AUDIBILITY_ENFORCED) == FLAG_AUDIBILITY_ENFORCED) {
- return AudioSystem.STREAM_SYSTEM_ENFORCED;
+ return fromGetVolumeControlStream ?
+ AudioSystem.STREAM_SYSTEM : AudioSystem.STREAM_SYSTEM_ENFORCED;
}
if ((aa.getFlags() & FLAG_SCO) == FLAG_SCO) {
- return AudioSystem.STREAM_BLUETOOTH_SCO;
+ return fromGetVolumeControlStream ?
+ AudioSystem.STREAM_VOICE_CALL : AudioSystem.STREAM_BLUETOOTH_SCO;
}
// usage to stream type mapping
@@ -753,7 +780,8 @@
case USAGE_VOICE_COMMUNICATION:
return AudioSystem.STREAM_VOICE_CALL;
case USAGE_VOICE_COMMUNICATION_SIGNALLING:
- return AudioSystem.STREAM_DTMF;
+ return fromGetVolumeControlStream ?
+ AudioSystem.STREAM_VOICE_CALL : AudioSystem.STREAM_DTMF;
case USAGE_ALARM:
return AudioSystem.STREAM_ALARM;
case USAGE_NOTIFICATION_RINGTONE:
@@ -765,8 +793,15 @@
case USAGE_NOTIFICATION_EVENT:
return AudioSystem.STREAM_NOTIFICATION;
case USAGE_UNKNOWN:
+ return fromGetVolumeControlStream ?
+ AudioManager.USE_DEFAULT_STREAM_TYPE : AudioSystem.STREAM_MUSIC;
default:
- return AudioSystem.STREAM_MUSIC;
+ if (fromGetVolumeControlStream) {
+ throw new IllegalArgumentException("Unknown usage value " + aa.getUsage() +
+ " in audio attributes");
+ } else {
+ return AudioSystem.STREAM_MUSIC;
+ }
}
}
diff --git a/media/java/android/media/AudioDeviceCallback.java b/media/java/android/media/AudioDeviceCallback.java
index d7fa492..d9f0037 100644
--- a/media/java/android/media/AudioDeviceCallback.java
+++ b/media/java/android/media/AudioDeviceCallback.java
@@ -17,16 +17,24 @@
package android.media;
/**
- * OnAudioDeviceConnectionListener defines the interface for notification listeners in the
- * {@link AudioManager}
+ * AudioDeviceCallback defines the mechanism by which applications can receive notifications
+ * of audio device connection and disconnection events.
+ * @see AudioManager#registerAudioDeviceCallback.
*/
public abstract class AudioDeviceCallback {
/**
- * Called by the {@link AudioManager} to indicate that an audio device has been
- * connected or disconnected. A listener will probably call the
- * {@link AudioManager#getDevices} method to retrieve the current list of audio
- * devices.
+ * Called by the {@link AudioManager} to indicate that one or more audio devices have been
+ * connected.
+ * @param addedDevices An array of {@link AudioDeviceInfo} objects corresponding to any
+ * newly added audio devices.
*/
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {}
+
+ /**
+ * Called by the {@link AudioManager} to indicate that one or more audio devices have been
+ * disconnected.
+ * @param removedDevices An array of {@link AudioDeviceInfo} objects corresponding to any
+ * newly removed audio devices.
+ */
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {}
}
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 2099bd0..431d37e 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -204,7 +204,7 @@
* @see AudioFormat
*/
public @NonNull int[] getEncodings() {
- return mPort.formats();
+ return AudioFormat.filterPublicFormats(mPort.formats());
}
/**
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index a7e092f..16ae58c 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
/**
* The AudioFormat class is used to access a number of audio format and
@@ -53,6 +54,22 @@
public static final int ENCODING_DTS = 7;
/** Audio data format: DTS HD compressed */
public static final int ENCODING_DTS_HD = 8;
+ /** Audio data format: MP3 compressed
+ * @hide
+ * */
+ public static final int ENCODING_MP3 = 9;
+ /** Audio data format: AAC LC compressed
+ * @hide
+ * */
+ public static final int ENCODING_AAC_LC = 10;
+ /** Audio data format: AAC HE V1 compressed
+ * @hide
+ * */
+ public static final int ENCODING_AAC_HE_V1 = 11;
+ /** Audio data format: AAC HE V2 compressed
+ * @hide
+ * */
+ public static final int ENCODING_AAC_HE_V2 = 12;
/** Invalid audio channel configuration */
/** @deprecated Use {@link #CHANNEL_INVALID} instead. */
@@ -241,6 +258,27 @@
case ENCODING_E_AC3:
case ENCODING_DTS:
case ENCODING_DTS_HD:
+ case ENCODING_MP3:
+ case ENCODING_AAC_LC:
+ case ENCODING_AAC_HE_V1:
+ case ENCODING_AAC_HE_V2:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /** @hide */
+ public static boolean isPublicEncoding(int audioFormat)
+ {
+ switch (audioFormat) {
+ case ENCODING_PCM_8BIT:
+ case ENCODING_PCM_16BIT:
+ case ENCODING_PCM_FLOAT:
+ case ENCODING_AC3:
+ case ENCODING_E_AC3:
+ case ENCODING_DTS:
+ case ENCODING_DTS_HD:
return true;
default:
return false;
@@ -260,6 +298,10 @@
case ENCODING_E_AC3:
case ENCODING_DTS:
case ENCODING_DTS_HD:
+ case ENCODING_MP3:
+ case ENCODING_AAC_LC:
+ case ENCODING_AAC_HE_V1:
+ case ENCODING_AAC_HE_V2:
return false;
case ENCODING_INVALID:
default:
@@ -267,6 +309,28 @@
}
}
+ /**
+ * Returns an array of public encoding values extracted from an array of
+ * encoding values.
+ * @hide
+ */
+ public static int[] filterPublicFormats(int[] formats) {
+ if (formats == null) {
+ return null;
+ }
+ int[] myCopy = Arrays.copyOf(formats, formats.length);
+ int size = 0;
+ for (int i = 0; i < myCopy.length; i++) {
+ if (isPublicEncoding(myCopy[i])) {
+ if (size != i) {
+ myCopy[size] = myCopy[i];
+ }
+ size++;
+ }
+ }
+ return Arrays.copyOf(myCopy, size);
+ }
+
/** @removed */
public AudioFormat()
{
@@ -503,7 +567,7 @@
* if both channel index mask and channel position mask
* are specified but do not have the same channel count.
*/
- public @NonNull Builder setChannelMask(int channelMask) throws IllegalArgumentException {
+ public @NonNull Builder setChannelMask(int channelMask) {
if (channelMask == 0) {
throw new IllegalArgumentException("Invalid zero channel mask");
} else if (/* channelMask != 0 && */ mChannelIndexMask != 0 &&
@@ -555,8 +619,7 @@
* if both channel index mask and channel position mask
* are specified but do not have the same channel count.
*/
- public @NonNull Builder setChannelIndexMask(int channelIndexMask)
- throws IllegalArgumentException {
+ public @NonNull Builder setChannelIndexMask(int channelIndexMask) {
if (channelIndexMask == 0) {
throw new IllegalArgumentException("Invalid zero channel index mask");
} else if (/* channelIndexMask != 0 && */ mChannelMask != 0 &&
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e7013a5..316ccf6 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3713,11 +3713,18 @@
private final static int MSG_DEVICES_DEVICES_ADDED = 0;
private final static int MSG_DEVICES_DEVICES_REMOVED = 1;
+ /**
+ * The list of {@link AudioDeviceCallback} objects to receive add/remove notifications.
+ */
private ArrayMap<AudioDeviceCallback, NativeEventHandlerDelegate>
mDeviceCallbacks =
new ArrayMap<AudioDeviceCallback, NativeEventHandlerDelegate>();
/**
+ * The following are flags to allow users of {@link AudioManager#getDevices(int)} to filter
+ * the results list to only those device types they are interested in.
+ */
+ /**
* Specifies to the {@link AudioManager#getDevices(int)} method to include
* source (i.e. input) audio devices.
*/
@@ -3747,21 +3754,25 @@
}
/**
- * Generates a list of AudioDeviceInfo objects corresponding to the audio devices currently
- * connected to the system and meeting the criteria specified in the <code>flags</code>
- * parameter.
+ * Returns an array of {@link AudioDeviceInfo} objects corresponding to the audio devices
+ * currently connected to the system and meeting the criteria specified in the
+ * <code>flags</code> parameter.
* @param flags A set of bitflags specifying the criteria to test.
- * @see {@link GET_DEVICES_OUTPUTS}, {@link GET_DEVICES_INPUTS} and {@lGET_DEVICES_CES_ALL}.
+ * @see {@link GET_DEVICES_OUTPUTS}, {@link GET_DEVICES_INPUTS} and {@link GET_DEVICES_ALL}.
* @return A (possibly zero-length) array of AudioDeviceInfo objects.
*/
public AudioDeviceInfo[] getDevices(int flags) {
return getDevicesStatic(flags);
}
+ /**
+ * Does the actual computation to generate an array of (externally-visible) AudioDeviceInfo
+ * objects from the current (internal) AudioDevicePort list.
+ */
private static AudioDeviceInfo[]
infoListFromPortList(ArrayList<AudioDevicePort> ports, int flags) {
- // figure out how many AudioDeviceInfo we need space for
+ // figure out how many AudioDeviceInfo we need space for...
int numRecs = 0;
for (AudioDevicePort port : ports) {
if (checkFlags(port, flags)) {
@@ -3769,7 +3780,7 @@
}
}
- // Now load them up
+ // Now load them up...
AudioDeviceInfo[] deviceList = new AudioDeviceInfo[numRecs];
int slot = 0;
for (AudioDevicePort port : ports) {
@@ -3782,7 +3793,12 @@
}
/*
- * Calculate the list of ports that are in ports_B, but not in ports_A
+ * Calculate the list of ports that are in ports_B, but not in ports_A. This is used by
+ * the add/remove callback mechanism to provide a list of the newly added or removed devices
+ * rather than the whole list and make the app figure it out.
+ * Note that calling this method with:
+ * ports_A == PREVIOUS_ports and ports_B == CURRENT_ports will calculated ADDED ports.
+ * ports_A == CURRENT_ports and ports_B == PREVIOUS_ports will calculated REMOVED ports.
*/
private static AudioDeviceInfo[] calcListDeltas(
ArrayList<AudioDevicePort> ports_A, ArrayList<AudioDevicePort> ports_B, int flags) {
@@ -3811,6 +3827,7 @@
* Generates a list of AudioDeviceInfo objects corresponding to the audio devices currently
* connected to the system and meeting the criteria specified in the <code>flags</code>
* parameter.
+ * This is an internal function. The public API front is getDevices(int).
* @param flags A set of bitflags specifying the criteria to test.
* @see {@link GET_DEVICES_OUTPUTS}, {@link GET_DEVICES_INPUTS} and {@link GET_DEVICES_ALL}.
* @return A (possibly zero-length) array of AudioDeviceInfo objects.
@@ -3821,15 +3838,20 @@
int status = AudioManager.listAudioDevicePorts(ports);
if (status != AudioManager.SUCCESS) {
// fail and bail!
- return new AudioDeviceInfo[0];
+ return new AudioDeviceInfo[0]; // Always return an array.
}
return infoListFromPortList(ports, flags);
}
/**
- * Adds an {@link AudioDeviceCallback} to receive notifications of changes
+ * Registers an {@link AudioDeviceCallback} object to receive notifications of changes
* to the set of connected audio devices.
+ * @param callback The {@link AudioDeviceCallback} object to receive connect/disconnect
+ * notifications.
+ * @param handler Specifies the {@link Handler} object for the thread on which to execute
+ * the callback. If <code>null</code>, the {@link Handler} associated with the main
+ * {@link Looper} will be used.
*/
public void registerAudioDeviceCallback(AudioDeviceCallback callback,
android.os.Handler handler) {
@@ -3842,8 +3864,10 @@
}
/**
- * Removes an {@link AudioDeviceCallback} which has been previously registered
+ * Unregisters an {@link AudioDeviceCallback} object which has been previously registered
* to receive notifications of changes to the set of connected audio devices.
+ * @param callback The {@link AudioDeviceCallback} object that was previously registered
+ * with {@link AudioManager#registerAudioDeviceCallback) to be unregistered.
*/
public void unregisterAudioDeviceCallback(AudioDeviceCallback callback) {
synchronized (mDeviceCallbacks) {
@@ -3854,7 +3878,8 @@
}
/**
- * Sends device list change notification to all listeners.
+ * Internal method to compute and generate add/remove messages and then send to any
+ * registered callbacks.
*/
private void broadcastDeviceListChange() {
int status;
@@ -3908,7 +3933,8 @@
/**
* Callback method called upon audio patch list update.
- * @param patchList the updated list of audio patches
+ * Note: We don't do anything with Patches at this time, so ignore this notification.
+ * @param patchList the updated list of audio patches.
*/
public void onAudioPatchListUpdate(AudioPatch[] patchList) {}
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index c0bc6d6..7eb1357 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -494,7 +494,7 @@
* than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum
* required buffer size for the successful creation of an AudioRecord instance.
* Since bufferSizeInBytes may be internally increased to accommodate the source
- * requirements, use {@link #getNativeFrameCount()} to determine the actual buffer size
+ * requirements, use {@link #getBufferSizeInFrames()} to determine the actual buffer size
* in frames.
* @param bufferSizeInBytes a value strictly greater than 0
* @return the same Builder instance.
@@ -777,7 +777,7 @@
}
/**
- * Returns the "native frame count" of the <code>AudioRecord</code> buffer.
+ * Returns the frame count of the native <code>AudioRecord</code> buffer.
* This is greater than or equal to the bufferSizeInBytes converted to frame units
* specified in the <code>AudioRecord</code> constructor or Builder.
* The native frame count may be enlarged to accommodate the requirements of the
@@ -786,8 +786,8 @@
* @return current size in frames of the <code>AudioRecord</code> buffer.
* @throws IllegalStateException
*/
- public int getNativeFrameCount() throws IllegalStateException {
- return native_get_native_frame_count();
+ public int getBufferSizeInFrames() {
+ return native_get_buffer_size_in_frames();
}
/**
@@ -1218,16 +1218,23 @@
//--------------------------------------------------------------------------
// (Re)Routing Info
//--------------------
+ /**
+ * Defines the interface by which applications can receive notifications of routing
+ * changes for the associated {@link AudioRecord}.
+ */
public interface OnRoutingChangedListener {
/**
* Called when the routing of an AudioRecord changes from either and explicit or
- * policy rerouting.
+ * policy rerouting. Use {@link #getRoutedDevice()} to retrieve the newly routed-from
+ * device.
*/
public void onRoutingChanged(AudioRecord audioRecord);
}
/**
* Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioRecord.
+ * Note: The query is only valid if the AudioRecord is currently recording. If it is not,
+ * <code>getRoutedDevice()</code> will return null.
*/
public AudioDeviceInfo getRoutedDevice() {
int deviceId = native_getRoutedDeviceId();
@@ -1245,8 +1252,9 @@
}
/**
- * The message sent to apps when the routing of this AudioRecord changes if they provide
- * a {#link Handler} object to addOnRoutingChangeListener().
+ * The list of AudioRecord.OnRoutingChangedListener interface added (with
+ * {@link AudioRecord#addOnRoutingChangedListener(OnRoutingChangedListener,android.os.Handler)}
+ * by an app to receive (re)routing notifications.
*/
private ArrayMap<OnRoutingChangedListener, NativeRoutingEventHandlerDelegate>
mRoutingChangeListeners =
@@ -1255,6 +1263,11 @@
/**
* Adds an {@link OnRoutingChangedListener} to receive notifications of routing changes
* on this AudioRecord.
+ * @param listener The {@link OnRoutingChangedListener} interface to receive notifications
+ * of rerouting events.
+ * @param handler Specifies the {@link Handler} object for the thread on which to execute
+ * the callback. If <code>null</code>, the {@link Handler} associated with the main
+ * {@link Looper} will be used.
*/
public void addOnRoutingChangedListener(OnRoutingChangedListener listener,
android.os.Handler handler) {
@@ -1271,7 +1284,8 @@
/**
* Removes an {@link OnRoutingChangedListener} which has been previously added
- * to receive notifications of changes to the set of connected audio devices.
+ * to receive rerouting notifications.
+ * @param listener The previously added {@link OnRoutingChangedListener} interface to remove.
*/
public void removeOnRoutingChangedListener(OnRoutingChangedListener listener) {
synchronized (mRoutingChangeListeners) {
@@ -1528,7 +1542,7 @@
private native final int native_read_in_direct_buffer(Object jBuffer,
int sizeInBytes, boolean isBlocking);
- private native final int native_get_native_frame_count();
+ private native final int native_get_buffer_size_in_frames();
private native final int native_set_marker_pos(int marker);
private native final int native_get_marker_pos();
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index d21762b..bd7a247 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -229,7 +229,7 @@
/**
* Sizes of the native audio buffer.
* These values are set during construction and can be stale.
- * To obtain the current native audio buffer frame count use {@link #getNativeFrameCount()}.
+ * To obtain the current native audio buffer frame count use {@link #getBufferSizeInFrames()}.
*/
private int mNativeBufferSizeInBytes = 0;
private int mNativeBufferSizeInFrames = 0;
@@ -346,7 +346,7 @@
* If <code>bufferSizeInBytes</code> is less than the
* minimum buffer size for the output sink, it is automatically increased to the minimum
* buffer size.
- * The method {@link #getNativeFrameCount()} returns the
+ * The method {@link #getBufferSizeInFrames()} returns the
* actual size in frames of the native buffer created, which
* determines the frequency to write
* to the streaming <code>AudioTrack</code> to avoid underrun.
@@ -775,7 +775,7 @@
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
}
- if (!AudioFormat.isValidEncoding(audioFormat)) {
+ if (!AudioFormat.isPublicEncoding(audioFormat)) {
throw new IllegalArgumentException("Unsupported audio encoding.");
}
mAudioFormat = audioFormat;
@@ -1009,7 +1009,7 @@
}
/**
- * Returns the "native frame count" of the <code>AudioTrack</code> buffer.
+ * Returns the frame count of the native <code>AudioTrack</code> buffer.
* <p> If the track's creation mode is {@link #MODE_STATIC},
* it is equal to the specified bufferSizeInBytes on construction, converted to frame units.
* A static track's native frame count will not change.
@@ -1019,12 +1019,26 @@
* the target output sink, and
* if the track is subsequently routed to a different output sink, the native
* frame count may enlarge to accommodate.
- * See also {@link AudioManager#getProperty(String)} for key
+ * <p> If the <code>AudioTrack</code> encoding indicates compressed data,
+ * e.g. {@link AudioFormat#ENCODING_AC3}, then the frame count returned is
+ * the size of the native <code>AudioTrack</code> buffer in bytes.
+ * <p> See also {@link AudioManager#getProperty(String)} for key
* {@link AudioManager#PROPERTY_OUTPUT_FRAMES_PER_BUFFER}.
- * @return current size in frames of the audio track buffer.
+ * @return current size in frames of the <code>AudioTrack</code> buffer.
* @throws IllegalStateException
*/
- public int getNativeFrameCount() throws IllegalStateException {
+ public int getBufferSizeInFrames() {
+ return native_get_native_frame_count();
+ }
+
+ /**
+ * Returns the frame count of the native <code>AudioTrack</code> buffer.
+ * @return current size in frames of the <code>AudioTrack</code> buffer.
+ * @throws IllegalStateException
+ * @deprecated Use the identical public method {@link #getBufferSizeInFrames()} instead.
+ */
+ @Deprecated
+ protected int getNativeFrameCount() {
return native_get_native_frame_count();
}
@@ -1119,7 +1133,7 @@
}
}
- if (!AudioFormat.isValidEncoding(audioFormat)) {
+ if (!AudioFormat.isPublicEncoding(audioFormat)) {
loge("getMinBufferSize(): Invalid audio format.");
return ERROR_BAD_VALUE;
}
@@ -1305,6 +1319,9 @@
* The valid sample rate range is from 1 Hz to twice the value returned by
* {@link #getNativeOutputSampleRate(int)}.
* Use {@link #setPlaybackParams(PlaybackParams)} for speed control.
+ * <p> This method may also be used to repurpose an existing <code>AudioTrack</code>
+ * for playback of content of differing sample rate,
+ * but with identical encoding and channel mask.
* @param sampleRateInHz the sample rate expressed in Hz
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
* {@link #ERROR_INVALID_OPERATION}
@@ -1474,7 +1491,7 @@
* <p>
* If the mode is {@link #MODE_STREAM}, you can optionally prime the data path prior to
* calling play(), by writing up to <code>bufferSizeInBytes</code> (from constructor).
- * If you don’t call write() first, or if you call write() but with an insufficient amount of
+ * If you don't call write() first, or if you call write() but with an insufficient amount of
* data, then the track will be in underrun state at play(). In this case,
* playback will not actually start playing until the data path is filled to a
* device-specific minimum level. This requirement for the path to be filled
@@ -2153,16 +2170,23 @@
//--------------------------------------------------------------------------
// (Re)Routing Info
//--------------------
+ /**
+ * Defines the interface by which applications can receive notifications of routing
+ * changes for the associated {@link AudioTrack}.
+ */
public interface OnRoutingChangedListener {
/**
* Called when the routing of an AudioTrack changes from either and explicit or
- * policy rerouting.
+ * policy rerouting. Use {@link #getRoutedDevice()} to retrieve the newly routed-to
+ * device.
*/
public void onRoutingChanged(AudioTrack audioTrack);
}
/**
* Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioTrack.
+ * Note: The query is only valid if the AudioTrack is currently playing. If it is not,
+ * <code>getRoutedDevice()</code> will return null.
*/
public AudioDeviceInfo getRoutedDevice() {
int deviceId = native_getRoutedDeviceId();
@@ -2180,8 +2204,9 @@
}
/**
- * The message sent to apps when the routing of this AudioTrack changes if they provide
- * a {#link Handler} object to addOnRoutingChangedListener().
+ * The list of AudioTrack.OnRoutingChangedListener interfaces added (with
+ * {@link AudioTrack#addOnRoutingChangedListener(OnRoutingChangedListener, android.os.Handler)}
+ * by an app to receive (re)routing notifications.
*/
private ArrayMap<OnRoutingChangedListener, NativeRoutingEventHandlerDelegate>
mRoutingChangeListeners =
@@ -2190,6 +2215,11 @@
/**
* Adds an {@link OnRoutingChangedListener} to receive notifications of routing changes
* on this AudioTrack.
+ * @param listener The {@link OnRoutingChangedListener} interface to receive notifications
+ * of rerouting events.
+ * @param handler Specifies the {@link Handler} object for the thread on which to execute
+ * the callback. If <code>null</code>, the {@link Handler} associated with the main
+ * {@link Looper} will be used.
*/
public void addOnRoutingChangedListener(OnRoutingChangedListener listener,
android.os.Handler handler) {
@@ -2206,7 +2236,8 @@
/**
* Removes an {@link OnRoutingChangedListener} which has been previously added
- * to receive notifications of changes to the set of connected audio devices.
+ * to receive rerouting notifications.
+ * @param listener The previously added {@link OnRoutingChangedListener} interface to remove.
*/
public void removeOnRoutingChangedListener(OnRoutingChangedListener listener) {
synchronized (mRoutingChangeListeners) {
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 66b904b..fed0ddf 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -173,10 +173,7 @@
break;
}
case DO_TIME_SHIFT_SET_PLAYBACK_PARAMS: {
- PlaybackParams params = new PlaybackParams()
- .setSpeed((Float) msg.obj)
- .setAudioFallbackMode(msg.arg1);
- mTvInputSessionImpl.timeShiftSetPlaybackParams(params);
+ mTvInputSessionImpl.timeShiftSetPlaybackParams((PlaybackParams) msg.obj);
break;
}
case DO_TIME_SHIFT_ENABLE_POSITION_TRACKING: {
diff --git a/packages/DocumentsUI/res/layout/fragment_pick.xml b/packages/DocumentsUI/res/layout/fragment_pick.xml
index 87dc4f8..40d4eb5 100644
--- a/packages/DocumentsUI/res/layout/fragment_pick.xml
+++ b/packages/DocumentsUI/res/layout/fragment_pick.xml
@@ -19,13 +19,13 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:baselineAligned="false"
- android:gravity="center_vertical"
+ android:gravity="end"
+ android:paddingEnd="8dp"
android:minHeight="?android:attr/listPreferredItemHeightSmall">
<Button
android:id="@android:id/button2"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_weight="1"
android:text="@android:string/cancel"
android:visibility="gone"
style="?android:attr/buttonBarNegativeButtonStyle" />
@@ -33,7 +33,5 @@
android:id="@android:id/button1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_weight="1"
- android:textAllCaps="false"
style="?android:attr/buttonBarPositiveButtonStyle" />
</LinearLayout>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 5281087..9794273 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -44,8 +44,6 @@
<string name="menu_share">Share</string>
<!-- Menu item title that deletes the selected documents [CHAR LIMIT=24] -->
<string name="menu_delete">Delete</string>
- <!-- Menu item title that selects the current directory [CHAR LIMIT=48] -->
- <string name="menu_select">Select \"<xliff:g id="directory" example="My Directory">^1</xliff:g>\"</string>
<!-- Menu item title that selects all documents in the current directory [CHAR LIMIT=24] -->
<string name="menu_select_all">Select All</string>
<!-- Menu item title that copies the selected documents [CHAR LIMIT=24] -->
@@ -65,7 +63,9 @@
<!-- Menu item that hides the sizes of displayed files [CHAR LIMIT=24] -->
<string name="menu_file_size_hide">Hide file size</string>
- <!-- Button label that copies files to the current directory [CHAR LIMIT=48] -->
+ <!-- Button label that select the current directory [CHAR LIMIT=24] -->
+ <string name="button_select">Select</string>
+ <!-- Button label that copies files to the current directory [CHAR LIMIT=24] -->
<string name="button_copy">Copy</string>
<!-- Action mode title summarizing the number of documents selected [CHAR LIMIT=32] -->
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index cded717..8482438 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -571,9 +571,7 @@
mState.action == ACTION_OPEN_COPY_DESTINATION) {
final PickFragment pick = PickFragment.get(fm);
if (pick != null) {
- final CharSequence displayName = (mState.stack.size() <= 1) ? root.title
- : cwd.displayName;
- pick.setPickTarget(mState.action, cwd, displayName);
+ pick.setPickTarget(mState.action, cwd);
}
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
index e899379..d9b8568 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
@@ -38,15 +38,19 @@
public class PickFragment extends Fragment {
public static final String TAG = "PickFragment";
+ private int mAction;
private DocumentInfo mPickTarget;
-
private View mContainer;
private Button mPick;
private Button mCancel;
public static void show(FragmentManager fm) {
- final PickFragment fragment = new PickFragment();
+ // Fragment can be restored by FragmentManager automatically.
+ if (get(fm) != null) {
+ return;
+ }
+ final PickFragment fragment = new PickFragment();
final FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.container_save, fragment, TAG);
ft.commitAllowingStateLoss();
@@ -67,8 +71,7 @@
mCancel = (Button) mContainer.findViewById(android.R.id.button2);
mCancel.setOnClickListener(mCancelListener);
- setPickTarget(0, null, null);
-
+ updateView();
return mContainer;
}
@@ -92,32 +95,38 @@
/**
* @param action Which action defined in BaseActivity.State is the picker shown for.
*/
- public void setPickTarget(int action,
- DocumentInfo pickTarget,
- CharSequence displayName) {
- if (mContainer != null) {
- if (pickTarget != null) {
- final Locale locale = getResources().getConfiguration().locale;
- switch (action) {
- case BaseActivity.State.ACTION_OPEN_TREE:
- final String raw = getString(R.string.menu_select).toUpperCase(locale);
- mPick.setText(TextUtils.expandTemplate(raw, displayName));
- mCancel.setVisibility(View.GONE);
- break;
- case BaseActivity.State.ACTION_OPEN_COPY_DESTINATION:
- mPick.setText(getString(R.string.button_copy).toUpperCase(locale));
- mCancel.setVisibility(View.VISIBLE);
- break;
- default:
- throw new IllegalArgumentException("Illegal action for PickFragment.");
- }
- }
- if (pickTarget != null && pickTarget.isCreateSupported()) {
- mContainer.setVisibility(View.VISIBLE);
- } else {
- mContainer.setVisibility(View.GONE);
- }
- }
+ public void setPickTarget(int action, DocumentInfo pickTarget) {
+ mAction = action;
mPickTarget = pickTarget;
+ if (mContainer != null) {
+ updateView();
+ }
+ }
+
+ /**
+ * Applies the state of fragment to the view components.
+ */
+ private void updateView() {
+ switch (mAction) {
+ case BaseActivity.State.ACTION_OPEN_TREE:
+ mPick.setText(R.string.button_select);
+ mCancel.setVisibility(View.GONE);
+ break;
+ case BaseActivity.State.ACTION_OPEN_COPY_DESTINATION:
+ mPick.setText(R.string.button_copy);
+ mCancel.setVisibility(View.VISIBLE);
+ break;
+ default:
+ mContainer.setVisibility(View.GONE);
+ return;
+ }
+
+ if (mPickTarget != null && (
+ mAction == BaseActivity.State.ACTION_OPEN_TREE ||
+ mPickTarget.isCreateSupported())) {
+ mContainer.setVisibility(View.VISIBLE);
+ } else {
+ mContainer.setVisibility(View.GONE);
+ }
}
}
diff --git a/packages/Keyguard/res/values-h650dp/dimens.xml b/packages/Keyguard/res/values-h650dp/dimens.xml
index 92035bb..1cd2162 100644
--- a/packages/Keyguard/res/values-h650dp/dimens.xml
+++ b/packages/Keyguard/res/values-h650dp/dimens.xml
@@ -16,5 +16,5 @@
-->
<resources>
- <dimen name="widget_big_font_size">104dp</dimen>
+ <dimen name="widget_big_font_size">100dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 5fece8d..5360645 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -361,9 +361,7 @@
Log.d(TAG, "Fingerprint disabled by DPM for userId: " + userId);
return;
}
- if (groupId == userId) {
- onFingerprintAuthenticated(groupId);
- }
+ onFingerprintAuthenticated(userId);
} finally {
setFingerprintRunningDetectionRunning(false);
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index ad710a6..1dba942 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -201,14 +201,14 @@
@GuardedBy("mLock")
private SettingsRegistry mSettingsRegistry;
- @GuardedBy("mLock")
- private UserManager mUserManager;
+ // We have to call in the user manager with no lock held,
+ private volatile UserManager mUserManager;
- @GuardedBy("mLock")
- private AppOpsManager mAppOpsManager;
+ // We have to call in the app ops manager with no lock held,
+ private volatile AppOpsManager mAppOpsManager;
- @GuardedBy("mLock")
- private PackageManager mPackageManager;
+ // We have to call in the package manager with no lock held,
+ private volatile PackageManager mPackageManager;
@Override
public boolean onCreate() {
@@ -224,44 +224,46 @@
@Override
public Bundle call(String method, String name, Bundle args) {
- synchronized (mLock) {
- final int requestingUserId = getRequestingUserId(args);
- switch (method) {
- case Settings.CALL_METHOD_GET_GLOBAL: {
- Setting setting = getGlobalSettingLocked(name);
- return packageValueForCallResult(setting);
- }
-
- case Settings.CALL_METHOD_GET_SECURE: {
- Setting setting = getSecureSettingLocked(name, requestingUserId);
- return packageValueForCallResult(setting);
- }
-
- case Settings.CALL_METHOD_GET_SYSTEM: {
- Setting setting = getSystemSettingLocked(name, requestingUserId);
- return packageValueForCallResult(setting);
- }
-
- case Settings.CALL_METHOD_PUT_GLOBAL: {
- String value = getSettingValue(args);
- insertGlobalSettingLocked(name, value, requestingUserId);
- } break;
-
- case Settings.CALL_METHOD_PUT_SECURE: {
- String value = getSettingValue(args);
- insertSecureSettingLocked(name, value, requestingUserId);
- } break;
-
- case Settings.CALL_METHOD_PUT_SYSTEM: {
- String value = getSettingValue(args);
- insertSystemSettingLocked(name, value, requestingUserId);
- } break;
-
- default: {
- Slog.w(LOG_TAG, "call() with invalid method: " + method);
- } break;
+ final int requestingUserId = getRequestingUserId(args);
+ switch (method) {
+ case Settings.CALL_METHOD_GET_GLOBAL: {
+ Setting setting = getGlobalSetting(name);
+ return packageValueForCallResult(setting);
}
+
+ case Settings.CALL_METHOD_GET_SECURE: {
+ Setting setting = getSecureSetting(name, requestingUserId);
+ return packageValueForCallResult(setting);
+ }
+
+ case Settings.CALL_METHOD_GET_SYSTEM: {
+ Setting setting = getSystemSetting(name, requestingUserId);
+ return packageValueForCallResult(setting);
+ }
+
+ case Settings.CALL_METHOD_PUT_GLOBAL: {
+ String value = getSettingValue(args);
+ insertGlobalSetting(name, value, requestingUserId);
+ break;
+ }
+
+ case Settings.CALL_METHOD_PUT_SECURE: {
+ String value = getSettingValue(args);
+ insertSecureSetting(name, value, requestingUserId);
+ break;
+ }
+
+ case Settings.CALL_METHOD_PUT_SYSTEM: {
+ String value = getSettingValue(args);
+ insertSystemSetting(name, value, requestingUserId);
+ break;
+ }
+
+ default: {
+ Slog.w(LOG_TAG, "call() with invalid method: " + method);
+ } break;
}
+
return null;
}
@@ -271,7 +273,7 @@
if (TextUtils.isEmpty(args.name)) {
return "vnd.android.cursor.dir/" + args.table;
} else {
- return "vnd.android.cursor.item/" + args.table;
+ return "vnd.android.cursor.item/" + args.table;
}
}
@@ -290,40 +292,38 @@
return new MatrixCursor(normalizedProjection, 0);
}
- synchronized (mLock) {
- switch (args.table) {
- case TABLE_GLOBAL: {
- if (args.name != null) {
- Setting setting = getGlobalSettingLocked(args.name);
- return packageSettingForQuery(setting, normalizedProjection);
- } else {
- return getAllGlobalSettingsLocked(projection);
- }
+ switch (args.table) {
+ case TABLE_GLOBAL: {
+ if (args.name != null) {
+ Setting setting = getGlobalSetting(args.name);
+ return packageSettingForQuery(setting, normalizedProjection);
+ } else {
+ return getAllGlobalSettings(projection);
}
+ }
- case TABLE_SECURE: {
- final int userId = UserHandle.getCallingUserId();
- if (args.name != null) {
- Setting setting = getSecureSettingLocked(args.name, userId);
- return packageSettingForQuery(setting, normalizedProjection);
- } else {
- return getAllSecureSettingsLocked(userId, projection);
- }
+ case TABLE_SECURE: {
+ final int userId = UserHandle.getCallingUserId();
+ if (args.name != null) {
+ Setting setting = getSecureSetting(args.name, userId);
+ return packageSettingForQuery(setting, normalizedProjection);
+ } else {
+ return getAllSecureSettings(userId, projection);
}
+ }
- case TABLE_SYSTEM: {
- final int userId = UserHandle.getCallingUserId();
- if (args.name != null) {
- Setting setting = getSystemSettingLocked(args.name, userId);
- return packageSettingForQuery(setting, normalizedProjection);
- } else {
- return getAllSystemSettingsLocked(userId, projection);
- }
+ case TABLE_SYSTEM: {
+ final int userId = UserHandle.getCallingUserId();
+ if (args.name != null) {
+ Setting setting = getSystemSetting(args.name, userId);
+ return packageSettingForQuery(setting, normalizedProjection);
+ } else {
+ return getAllSystemSettings(userId, projection);
}
+ }
- default: {
- throw new IllegalArgumentException("Invalid Uri path:" + uri);
- }
+ default: {
+ throw new IllegalArgumentException("Invalid Uri path:" + uri);
}
}
}
@@ -348,29 +348,27 @@
String value = values.getAsString(Settings.Secure.VALUE);
- synchronized (mLock) {
- switch (table) {
- case TABLE_GLOBAL: {
- if (insertGlobalSettingLocked(name, value, UserHandle.getCallingUserId())) {
- return Uri.withAppendedPath(Settings.Global.CONTENT_URI, name);
- }
- } break;
-
- case TABLE_SECURE: {
- if (insertSecureSettingLocked(name, value, UserHandle.getCallingUserId())) {
- return Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name);
- }
- } break;
-
- case TABLE_SYSTEM: {
- if (insertSystemSettingLocked(name, value, UserHandle.getCallingUserId())) {
- return Uri.withAppendedPath(Settings.System.CONTENT_URI, name);
- }
- } break;
-
- default: {
- throw new IllegalArgumentException("Bad Uri path:" + uri);
+ switch (table) {
+ case TABLE_GLOBAL: {
+ if (insertGlobalSetting(name, value, UserHandle.getCallingUserId())) {
+ return Uri.withAppendedPath(Settings.Global.CONTENT_URI, name);
}
+ } break;
+
+ case TABLE_SECURE: {
+ if (insertSecureSetting(name, value, UserHandle.getCallingUserId())) {
+ return Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name);
+ }
+ } break;
+
+ case TABLE_SYSTEM: {
+ if (insertSystemSetting(name, value, UserHandle.getCallingUserId())) {
+ return Uri.withAppendedPath(Settings.System.CONTENT_URI, name);
+ }
+ } break;
+
+ default: {
+ throw new IllegalArgumentException("Bad Uri path:" + uri);
}
}
@@ -412,26 +410,25 @@
return 0;
}
- synchronized (mLock) {
- switch (args.table) {
- case TABLE_GLOBAL: {
- final int userId = UserHandle.getCallingUserId();
- return deleteGlobalSettingLocked(args.name, userId) ? 1 : 0;
- }
- case TABLE_SECURE: {
- final int userId = UserHandle.getCallingUserId();
- return deleteSecureSettingLocked(args.name, userId) ? 1 : 0;
- }
+ switch (args.table) {
+ case TABLE_GLOBAL: {
+ final int userId = UserHandle.getCallingUserId();
+ return deleteGlobalSetting(args.name, userId) ? 1 : 0;
+ }
- case TABLE_SYSTEM: {
- final int userId = UserHandle.getCallingUserId();
- return deleteSystemSettingLocked(args.name, userId) ? 1 : 0;
- }
+ case TABLE_SECURE: {
+ final int userId = UserHandle.getCallingUserId();
+ return deleteSecureSetting(args.name, userId) ? 1 : 0;
+ }
- default: {
- throw new IllegalArgumentException("Bad Uri path:" + uri);
- }
+ case TABLE_SYSTEM: {
+ final int userId = UserHandle.getCallingUserId();
+ return deleteSystemSetting(args.name, userId) ? 1 : 0;
+ }
+
+ default: {
+ throw new IllegalArgumentException("Bad Uri path:" + uri);
}
}
}
@@ -454,26 +451,24 @@
return 0;
}
- synchronized (mLock) {
- switch (args.table) {
- case TABLE_GLOBAL: {
- final int userId = UserHandle.getCallingUserId();
- return updateGlobalSettingLocked(args.name, value, userId) ? 1 : 0;
- }
+ switch (args.table) {
+ case TABLE_GLOBAL: {
+ final int userId = UserHandle.getCallingUserId();
+ return updateGlobalSetting(args.name, value, userId) ? 1 : 0;
+ }
- case TABLE_SECURE: {
- final int userId = UserHandle.getCallingUserId();
- return updateSecureSettingLocked(args.name, value, userId) ? 1 : 0;
- }
+ case TABLE_SECURE: {
+ final int userId = UserHandle.getCallingUserId();
+ return updateSecureSetting(args.name, value, userId) ? 1 : 0;
+ }
- case TABLE_SYSTEM: {
- final int userId = UserHandle.getCallingUserId();
- return updateSystemSettingLocked(args.name, value, userId) ? 1 : 0;
- }
+ case TABLE_SYSTEM: {
+ final int userId = UserHandle.getCallingUserId();
+ return updateSystemSetting(args.name, value, userId) ? 1 : 0;
+ }
- default: {
- throw new IllegalArgumentException("Invalid Uri path:" + uri);
- }
+ default: {
+ throw new IllegalArgumentException("Invalid Uri path:" + uri);
}
}
}
@@ -504,18 +499,18 @@
private void dumpForUser(int userId, PrintWriter pw) {
if (userId == UserHandle.USER_OWNER) {
pw.println("GLOBAL SETTINGS (user " + userId + ")");
- Cursor globalCursor = getAllGlobalSettingsLocked(ALL_COLUMNS);
+ Cursor globalCursor = getAllGlobalSettings(ALL_COLUMNS);
dumpSettings(globalCursor, pw);
pw.println();
}
pw.println("SECURE SETTINGS (user " + userId + ")");
- Cursor secureCursor = getAllSecureSettingsLocked(userId, ALL_COLUMNS);
+ Cursor secureCursor = getAllSecureSettings(userId, ALL_COLUMNS);
dumpSettings(secureCursor, pw);
pw.println();
pw.println("SYSTEM SETTINGS (user " + userId + ")");
- Cursor systemCursor = getAllSystemSettingsLocked(userId, ALL_COLUMNS);
+ Cursor systemCursor = getAllSystemSettings(userId, ALL_COLUMNS);
dumpSettings(systemCursor, pw);
pw.println();
}
@@ -575,64 +570,68 @@
UserHandle.ALL, true);
}
- private Cursor getAllGlobalSettingsLocked(String[] projection) {
+ private Cursor getAllGlobalSettings(String[] projection) {
if (DEBUG) {
- Slog.v(LOG_TAG, "getAllGlobalSettingsLocked()");
+ Slog.v(LOG_TAG, "getAllGlobalSettings()");
}
- // Get the settings.
- SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
- SettingsRegistry.SETTINGS_TYPE_GLOBAL, UserHandle.USER_OWNER);
+ synchronized (mLock) {
+ // Get the settings.
+ SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
+ SettingsRegistry.SETTINGS_TYPE_GLOBAL, UserHandle.USER_OWNER);
- List<String> names = settingsState.getSettingNamesLocked();
+ List<String> names = settingsState.getSettingNamesLocked();
- final int nameCount = names.size();
+ final int nameCount = names.size();
- String[] normalizedProjection = normalizeProjection(projection);
- MatrixCursor result = new MatrixCursor(normalizedProjection, nameCount);
+ String[] normalizedProjection = normalizeProjection(projection);
+ MatrixCursor result = new MatrixCursor(normalizedProjection, nameCount);
- // Anyone can get the global settings, so no security checks.
- for (int i = 0; i < nameCount; i++) {
- String name = names.get(i);
- Setting setting = settingsState.getSettingLocked(name);
- appendSettingToCursor(result, setting);
+ // Anyone can get the global settings, so no security checks.
+ for (int i = 0; i < nameCount; i++) {
+ String name = names.get(i);
+ Setting setting = settingsState.getSettingLocked(name);
+ appendSettingToCursor(result, setting);
+ }
+
+ return result;
}
-
- return result;
}
- private Setting getGlobalSettingLocked(String name) {
+ private Setting getGlobalSetting(String name) {
if (DEBUG) {
Slog.v(LOG_TAG, "getGlobalSetting(" + name + ")");
}
// Get the value.
- return mSettingsRegistry.getSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL,
- UserHandle.USER_OWNER, name);
- }
-
- private boolean updateGlobalSettingLocked(String name, String value, int requestingUserId) {
- if (DEBUG) {
- Slog.v(LOG_TAG, "updateGlobalSettingLocked(" + name + ", " + value + ")");
+ synchronized (mLock) {
+ return mSettingsRegistry.getSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL,
+ UserHandle.USER_OWNER, name);
}
- return mutateGlobalSettingLocked(name, value, requestingUserId, MUTATION_OPERATION_UPDATE);
}
- private boolean insertGlobalSettingLocked(String name, String value, int requestingUserId) {
+ private boolean updateGlobalSetting(String name, String value, int requestingUserId) {
if (DEBUG) {
- Slog.v(LOG_TAG, "insertGlobalSettingLocked(" + name + ", " + value + ")");
+ Slog.v(LOG_TAG, "updateGlobalSetting(" + name + ", " + value + ")");
}
- return mutateGlobalSettingLocked(name, value, requestingUserId, MUTATION_OPERATION_INSERT);
+ return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE);
}
- private boolean deleteGlobalSettingLocked(String name, int requestingUserId) {
+ private boolean insertGlobalSetting(String name, String value, int requestingUserId) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "insertGlobalSetting(" + name + ", " + value + ")");
+ }
+ return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT);
+ }
+
+ private boolean deleteGlobalSetting(String name, int requestingUserId) {
if (DEBUG) {
Slog.v(LOG_TAG, "deleteGlobalSettingLocked(" + name + ")");
}
- return mutateGlobalSettingLocked(name, null, requestingUserId, MUTATION_OPERATION_DELETE);
+ return mutateGlobalSetting(name, null, requestingUserId, MUTATION_OPERATION_DELETE);
}
- private boolean mutateGlobalSettingLocked(String name, String value, int requestingUserId,
+ private boolean mutateGlobalSetting(String name, String value, int requestingUserId,
int operation) {
// Make sure the caller can change the settings - treated as secure.
enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
@@ -651,28 +650,32 @@
}
// Perform the mutation.
- switch (operation) {
- case MUTATION_OPERATION_INSERT: {
- return mSettingsRegistry.insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL,
- UserHandle.USER_OWNER, name, value, getCallingPackage());
- }
+ synchronized (mLock) {
+ switch (operation) {
+ case MUTATION_OPERATION_INSERT: {
+ return mSettingsRegistry
+ .insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL,
+ UserHandle.USER_OWNER, name, value, getCallingPackage());
+ }
- case MUTATION_OPERATION_DELETE: {
- return mSettingsRegistry.deleteSettingLocked(
- SettingsRegistry.SETTINGS_TYPE_GLOBAL,
- UserHandle.USER_OWNER, name);
- }
+ case MUTATION_OPERATION_DELETE: {
+ return mSettingsRegistry.deleteSettingLocked(
+ SettingsRegistry.SETTINGS_TYPE_GLOBAL,
+ UserHandle.USER_OWNER, name);
+ }
- case MUTATION_OPERATION_UPDATE: {
- return mSettingsRegistry.updateSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL,
- UserHandle.USER_OWNER, name, value, getCallingPackage());
+ case MUTATION_OPERATION_UPDATE: {
+ return mSettingsRegistry
+ .updateSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL,
+ UserHandle.USER_OWNER, name, value, getCallingPackage());
+ }
}
}
return false;
}
- private Cursor getAllSecureSettingsLocked(int userId, String[] projection) {
+ private Cursor getAllSecureSettings(int userId, String[] projection) {
if (DEBUG) {
Slog.v(LOG_TAG, "getAllSecureSettings(" + userId + ")");
}
@@ -680,34 +683,36 @@
// Resolve the userId on whose behalf the call is made.
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId);
- List<String> names = mSettingsRegistry.getSettingsNamesLocked(
- SettingsRegistry.SETTINGS_TYPE_SECURE, callingUserId);
+ synchronized (mLock) {
+ List<String> names = mSettingsRegistry.getSettingsNamesLocked(
+ SettingsRegistry.SETTINGS_TYPE_SECURE, callingUserId);
- final int nameCount = names.size();
+ final int nameCount = names.size();
- String[] normalizedProjection = normalizeProjection(projection);
- MatrixCursor result = new MatrixCursor(normalizedProjection, nameCount);
+ String[] normalizedProjection = normalizeProjection(projection);
+ MatrixCursor result = new MatrixCursor(normalizedProjection, nameCount);
- for (int i = 0; i < nameCount; i++) {
- String name = names.get(i);
+ for (int i = 0; i < nameCount; i++) {
+ String name = names.get(i);
+ // Determine the owning user as some profile settings are cloned from the parent.
+ final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId,
+ name);
- // Determine the owning user as some profile settings are cloned from the parent.
- final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, name);
+ // Special case for location (sigh).
+ if (isLocationProvidersAllowedRestricted(name, callingUserId, owningUserId)) {
+ return null;
+ }
- // Special case for location (sigh).
- if (isLocationProvidersAllowedRestricted(name, callingUserId, owningUserId)) {
- return null;
+ Setting setting = mSettingsRegistry.getSettingLocked(
+ SettingsRegistry.SETTINGS_TYPE_SECURE, owningUserId, name);
+ appendSettingToCursor(result, setting);
}
- Setting setting = mSettingsRegistry.getSettingLocked(
- SettingsRegistry.SETTINGS_TYPE_SECURE, owningUserId, name);
- appendSettingToCursor(result, setting);
+ return result;
}
-
- return result;
}
- private Setting getSecureSettingLocked(String name, int requestingUserId) {
+ private Setting getSecureSetting(String name, int requestingUserId) {
if (DEBUG) {
Slog.v(LOG_TAG, "getSecureSetting(" + name + ", " + requestingUserId + ")");
}
@@ -724,37 +729,39 @@
}
// Get the value.
- return mSettingsRegistry.getSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE,
- owningUserId, name);
+ synchronized (mLock) {
+ return mSettingsRegistry.getSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE,
+ owningUserId, name);
+ }
}
- private boolean insertSecureSettingLocked(String name, String value, int requestingUserId) {
+ private boolean insertSecureSetting(String name, String value, int requestingUserId) {
if (DEBUG) {
- Slog.v(LOG_TAG, "insertSecureSettingLocked(" + name + ", " + value + ", "
+ Slog.v(LOG_TAG, "insertSecureSetting(" + name + ", " + value + ", "
+ requestingUserId + ")");
}
- return mutateSecureSettingLocked(name, value, requestingUserId, MUTATION_OPERATION_INSERT);
+ return mutateSecureSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT);
}
- private boolean deleteSecureSettingLocked(String name, int requestingUserId) {
+ private boolean deleteSecureSetting(String name, int requestingUserId) {
if (DEBUG) {
- Slog.v(LOG_TAG, "deleteSecureSettingLocked(" + name + ", " + requestingUserId + ")");
+ Slog.v(LOG_TAG, "deleteSecureSetting(" + name + ", " + requestingUserId + ")");
}
- return mutateSecureSettingLocked(name, null, requestingUserId, MUTATION_OPERATION_DELETE);
+ return mutateSecureSetting(name, null, requestingUserId, MUTATION_OPERATION_DELETE);
}
- private boolean updateSecureSettingLocked(String name, String value, int requestingUserId) {
+ private boolean updateSecureSetting(String name, String value, int requestingUserId) {
if (DEBUG) {
- Slog.v(LOG_TAG, "updateSecureSettingLocked(" + name + ", " + value + ", "
+ Slog.v(LOG_TAG, "updateSecureSetting(" + name + ", " + value + ", "
+ requestingUserId + ")");
}
- return mutateSecureSettingLocked(name, value, requestingUserId, MUTATION_OPERATION_UPDATE);
+ return mutateSecureSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE);
}
- private boolean mutateSecureSettingLocked(String name, String value, int requestingUserId,
+ private boolean mutateSecureSetting(String name, String value, int requestingUserId,
int operation) {
// Make sure the caller can change the settings.
enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
@@ -786,58 +793,65 @@
}
// Mutate the value.
- switch(operation) {
- case MUTATION_OPERATION_INSERT: {
- return mSettingsRegistry.insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE,
- owningUserId, name, value, getCallingPackage());
- }
+ synchronized (mLock) {
+ switch (operation) {
+ case MUTATION_OPERATION_INSERT: {
+ return mSettingsRegistry
+ .insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE,
+ owningUserId, name, value, getCallingPackage());
+ }
- case MUTATION_OPERATION_DELETE: {
- return mSettingsRegistry.deleteSettingLocked(
- SettingsRegistry.SETTINGS_TYPE_SECURE,
- owningUserId, name);
- }
+ case MUTATION_OPERATION_DELETE: {
+ return mSettingsRegistry.deleteSettingLocked(
+ SettingsRegistry.SETTINGS_TYPE_SECURE,
+ owningUserId, name);
+ }
- case MUTATION_OPERATION_UPDATE: {
- return mSettingsRegistry.updateSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE,
- owningUserId, name, value, getCallingPackage());
+ case MUTATION_OPERATION_UPDATE: {
+ return mSettingsRegistry
+ .updateSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE,
+ owningUserId, name, value, getCallingPackage());
+ }
}
}
return false;
}
- private Cursor getAllSystemSettingsLocked(int userId, String[] projection) {
+ private Cursor getAllSystemSettings(int userId, String[] projection) {
if (DEBUG) {
- Slog.v(LOG_TAG, "getAllSecureSystemLocked(" + userId + ")");
+ Slog.v(LOG_TAG, "getAllSecureSystem(" + userId + ")");
}
// Resolve the userId on whose behalf the call is made.
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId);
- List<String> names = mSettingsRegistry.getSettingsNamesLocked(
- SettingsRegistry.SETTINGS_TYPE_SYSTEM, callingUserId);
+ synchronized (mLock) {
+ List<String> names = mSettingsRegistry.getSettingsNamesLocked(
+ SettingsRegistry.SETTINGS_TYPE_SYSTEM, callingUserId);
- final int nameCount = names.size();
+ final int nameCount = names.size();
- String[] normalizedProjection = normalizeProjection(projection);
- MatrixCursor result = new MatrixCursor(normalizedProjection, nameCount);
+ String[] normalizedProjection = normalizeProjection(projection);
+ MatrixCursor result = new MatrixCursor(normalizedProjection, nameCount);
- for (int i = 0; i < nameCount; i++) {
- String name = names.get(i);
+ for (int i = 0; i < nameCount; i++) {
+ String name = names.get(i);
- // Determine the owning user as some profile settings are cloned from the parent.
- final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId, name);
+ // Determine the owning user as some profile settings are cloned from the parent.
+ final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId,
+ name);
- Setting setting = mSettingsRegistry.getSettingLocked(
- SettingsRegistry.SETTINGS_TYPE_SYSTEM, owningUserId, name);
- appendSettingToCursor(result, setting);
+ Setting setting = mSettingsRegistry.getSettingLocked(
+ SettingsRegistry.SETTINGS_TYPE_SYSTEM, owningUserId, name);
+ appendSettingToCursor(result, setting);
+ }
+
+ return result;
}
-
- return result;
}
- private Setting getSystemSettingLocked(String name, int requestingUserId) {
+ private Setting getSystemSetting(String name, int requestingUserId) {
if (DEBUG) {
Slog.v(LOG_TAG, "getSystemSetting(" + name + ", " + requestingUserId + ")");
}
@@ -849,37 +863,39 @@
final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId, name);
// Get the value.
- return mSettingsRegistry.getSettingLocked(SettingsRegistry.SETTINGS_TYPE_SYSTEM,
- owningUserId, name);
+ synchronized (mLock) {
+ return mSettingsRegistry.getSettingLocked(SettingsRegistry.SETTINGS_TYPE_SYSTEM,
+ owningUserId, name);
+ }
}
- private boolean insertSystemSettingLocked(String name, String value, int requestingUserId) {
+ private boolean insertSystemSetting(String name, String value, int requestingUserId) {
if (DEBUG) {
- Slog.v(LOG_TAG, "insertSystemSettingLocked(" + name + ", " + value + ", "
+ Slog.v(LOG_TAG, "insertSystemSetting(" + name + ", " + value + ", "
+ requestingUserId + ")");
}
- return mutateSystemSettingLocked(name, value, requestingUserId, MUTATION_OPERATION_INSERT);
+ return mutateSystemSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT);
}
- private boolean deleteSystemSettingLocked(String name, int requestingUserId) {
+ private boolean deleteSystemSetting(String name, int requestingUserId) {
if (DEBUG) {
- Slog.v(LOG_TAG, "deleteSystemSettingLocked(" + name + ", " + requestingUserId + ")");
+ Slog.v(LOG_TAG, "deleteSystemSetting(" + name + ", " + requestingUserId + ")");
}
- return mutateSystemSettingLocked(name, null, requestingUserId, MUTATION_OPERATION_DELETE);
+ return mutateSystemSetting(name, null, requestingUserId, MUTATION_OPERATION_DELETE);
}
- private boolean updateSystemSettingLocked(String name, String value, int requestingUserId) {
+ private boolean updateSystemSetting(String name, String value, int requestingUserId) {
if (DEBUG) {
- Slog.v(LOG_TAG, "updateSystemSettingLocked(" + name + ", " + value + ", "
+ Slog.v(LOG_TAG, "updateSystemSetting(" + name + ", " + value + ", "
+ requestingUserId + ")");
}
- return mutateSystemSettingLocked(name, value, requestingUserId, MUTATION_OPERATION_UPDATE);
+ return mutateSystemSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE);
}
- private boolean mutateSystemSettingLocked(String name, String value, int runAsUserId,
+ private boolean mutateSystemSetting(String name, String value, int runAsUserId,
int operation) {
// Make sure the caller can change the settings.
enforceWritePermission(Manifest.permission.WRITE_SETTINGS);
@@ -904,27 +920,31 @@
}
// Mutate the value.
- switch (operation) {
- case MUTATION_OPERATION_INSERT: {
- validateSystemSettingValue(name, value);
- return mSettingsRegistry.insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_SYSTEM,
- owningUserId, name, value, getCallingPackage());
+ synchronized (mLock) {
+ switch (operation) {
+ case MUTATION_OPERATION_INSERT: {
+ validateSystemSettingValue(name, value);
+ return mSettingsRegistry
+ .insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_SYSTEM,
+ owningUserId, name, value, getCallingPackage());
+ }
+
+ case MUTATION_OPERATION_DELETE: {
+ return mSettingsRegistry.deleteSettingLocked(
+ SettingsRegistry.SETTINGS_TYPE_SYSTEM,
+ owningUserId, name);
+ }
+
+ case MUTATION_OPERATION_UPDATE: {
+ validateSystemSettingValue(name, value);
+ return mSettingsRegistry
+ .updateSettingLocked(SettingsRegistry.SETTINGS_TYPE_SYSTEM,
+ owningUserId, name, value, getCallingPackage());
+ }
}
- case MUTATION_OPERATION_DELETE: {
- return mSettingsRegistry.deleteSettingLocked(
- SettingsRegistry.SETTINGS_TYPE_SYSTEM,
- owningUserId, name);
- }
-
- case MUTATION_OPERATION_UPDATE: {
- validateSystemSettingValue(name, value);
- return mSettingsRegistry.updateSettingLocked(SettingsRegistry.SETTINGS_TYPE_SYSTEM,
- owningUserId, name, value, getCallingPackage());
- }
+ return false;
}
-
- return false;
}
private void validateSystemSettingValue(String name, String value) {
@@ -1043,6 +1063,7 @@
// user info is a cached instance, so just look up instead of cache.
final long identity = Binder.clearCallingIdentity();
try {
+ // Just a lookup and not reentrant, so holding a lock is fine.
UserInfo userInfo = mUserManager.getProfileParent(userId);
return (userInfo != null) ? userInfo.id : userId;
} finally {
@@ -1088,7 +1109,7 @@
// skip prefix
value = value.substring(1);
- Setting settingValue = getSecureSettingLocked(
+ Setting settingValue = getSecureSetting(
Settings.Secure.LOCATION_PROVIDERS_ALLOWED, owningUserId);
String oldProviders = (settingValue != null) ? settingValue.getValue() : "";
diff --git a/packages/StatementService/src/com/android/statementservice/retriever/AbstractAsset.java b/packages/StatementService/src/com/android/statementservice/retriever/AbstractAsset.java
index e71cf54..bb6bdbb 100644
--- a/packages/StatementService/src/com/android/statementservice/retriever/AbstractAsset.java
+++ b/packages/StatementService/src/com/android/statementservice/retriever/AbstractAsset.java
@@ -63,4 +63,10 @@
throws AssociationServiceException {
return AssetFactory.create(assetJson);
}
+
+ /**
+ * If this is the source asset of a statement file, should the retriever follow
+ * any insecure (non-HTTPS) include statements made by the asset.
+ */
+ public abstract boolean followInsecureInclude();
}
diff --git a/packages/StatementService/src/com/android/statementservice/retriever/AndroidAppAsset.java b/packages/StatementService/src/com/android/statementservice/retriever/AndroidAppAsset.java
index 0c96038..8ead90b 100644
--- a/packages/StatementService/src/com/android/statementservice/retriever/AndroidAppAsset.java
+++ b/packages/StatementService/src/com/android/statementservice/retriever/AndroidAppAsset.java
@@ -99,6 +99,12 @@
return getPackageName().hashCode();
}
+ @Override
+ public boolean followInsecureInclude() {
+ // Non-HTTPS includes are not allowed in Android App assets.
+ return false;
+ }
+
/**
* Checks that the input is a valid Android app asset.
*
diff --git a/packages/StatementService/src/com/android/statementservice/retriever/DirectStatementRetriever.java b/packages/StatementService/src/com/android/statementservice/retriever/DirectStatementRetriever.java
index 6516516..548149e 100644
--- a/packages/StatementService/src/com/android/statementservice/retriever/DirectStatementRetriever.java
+++ b/packages/StatementService/src/com/android/statementservice/retriever/DirectStatementRetriever.java
@@ -136,7 +136,8 @@
}
}
- private Result retrieveStatementFromUrl(String url, int maxIncludeLevel, AbstractAsset source)
+ private Result retrieveStatementFromUrl(String urlString, int maxIncludeLevel,
+ AbstractAsset source)
throws AssociationServiceException {
List<Statement> statements = new ArrayList<Statement>();
if (maxIncludeLevel < 0) {
@@ -145,7 +146,12 @@
WebContent webContent;
try {
- webContent = mUrlFetcher.getWebContentFromUrl(new URL(url),
+ URL url = new URL(urlString);
+ if (!source.followInsecureInclude()
+ && !url.getProtocol().toLowerCase().equals("https")) {
+ return Result.create(statements, DO_NOT_CACHE_RESULT);
+ }
+ webContent = mUrlFetcher.getWebContentFromUrl(url,
HTTP_CONTENT_SIZE_LIMIT_IN_BYTES, HTTP_CONNECTION_TIMEOUT_MILLIS);
} catch (IOException e) {
return Result.create(statements, DO_NOT_CACHE_RESULT);
diff --git a/packages/StatementService/src/com/android/statementservice/retriever/URLFetcher.java b/packages/StatementService/src/com/android/statementservice/retriever/URLFetcher.java
index 4828ff9..969aa88 100644
--- a/packages/StatementService/src/com/android/statementservice/retriever/URLFetcher.java
+++ b/packages/StatementService/src/com/android/statementservice/retriever/URLFetcher.java
@@ -16,6 +16,8 @@
package com.android.statementservice.retriever;
+import android.util.Log;
+
import com.android.volley.Cache;
import com.android.volley.NetworkResponse;
import com.android.volley.toolbox.HttpHeaderParser;
@@ -39,6 +41,7 @@
* @hide
*/
public class URLFetcher {
+ private static final String TAG = URLFetcher.class.getSimpleName();
private static final long DO_NOT_CACHE_RESULT = 0L;
private static final int INPUT_BUFFER_SIZE_IN_BYTES = 1024;
@@ -63,11 +66,17 @@
connection.setConnectTimeout(connectionTimeoutMillis);
connection.setReadTimeout(connectionTimeoutMillis);
connection.setUseCaches(true);
+ connection.setInstanceFollowRedirects(false);
connection.addRequestProperty("Cache-Control", "max-stale=60");
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ Log.e(TAG, "The responses code is not 200 but " + connection.getResponseCode());
+ return new WebContent("", DO_NOT_CACHE_RESULT);
+ }
+
if (connection.getContentLength() > fileSizeLimit) {
- throw new AssociationServiceException("The content size of the url is larger than "
- + fileSizeLimit);
+ Log.e(TAG, "The content size of the url is larger than " + fileSizeLimit);
+ return new WebContent("", DO_NOT_CACHE_RESULT);
}
Long expireTimeMillis = getExpirationTimeMillisFromHTTPHeader(connection.getHeaderFields());
diff --git a/packages/StatementService/src/com/android/statementservice/retriever/Utils.java b/packages/StatementService/src/com/android/statementservice/retriever/Utils.java
index 44af864..afb4c75 100644
--- a/packages/StatementService/src/com/android/statementservice/retriever/Utils.java
+++ b/packages/StatementService/src/com/android/statementservice/retriever/Utils.java
@@ -61,7 +61,7 @@
*/
public static final String ASSET_DESCRIPTOR_FIELD_RELATION = "relation";
public static final String ASSET_DESCRIPTOR_FIELD_TARGET = "target";
- public static final String DELEGATE_FIELD_DELEGATE = "delegate";
+ public static final String DELEGATE_FIELD_DELEGATE = "include";
private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F' };
diff --git a/packages/StatementService/src/com/android/statementservice/retriever/WebAsset.java b/packages/StatementService/src/com/android/statementservice/retriever/WebAsset.java
index ca9e62d..947087a 100644
--- a/packages/StatementService/src/com/android/statementservice/retriever/WebAsset.java
+++ b/packages/StatementService/src/com/android/statementservice/retriever/WebAsset.java
@@ -39,6 +39,7 @@
/* package private */ final class WebAsset extends AbstractAsset {
private static final String MISSING_FIELD_FORMAT_STRING = "Expected %s to be set.";
+ private static final String SCHEME_HTTP = "http";
private final URL mUrl;
@@ -105,6 +106,12 @@
return toJson().hashCode();
}
+ @Override
+ public boolean followInsecureInclude() {
+ // Only allow insecure include file if the asset scheme is http.
+ return SCHEME_HTTP.equals(getScheme());
+ }
+
/**
* Checks that the input is a valid web asset.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 9ef9211..c10be7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -117,6 +117,7 @@
* intercepted yet.
*/
private boolean mIntercepting;
+ private boolean mPanelExpanded;
private boolean mQsExpanded;
private boolean mQsExpandedWhenExpandingStarted;
private boolean mQsFullyExpanded;
@@ -1496,13 +1497,22 @@
updateHeader();
updateUnlockIcon();
updateNotificationTranslucency();
- mHeadsUpManager.setIsExpanded(!isFullyCollapsed());
+ updatePanelExpanded();
mNotificationStackScroller.setShadeExpanded(!isFullyCollapsed());
if (DEBUG) {
invalidate();
}
}
+ private void updatePanelExpanded() {
+ boolean isExpanded = !isFullyCollapsed();
+ if (mPanelExpanded != isExpanded) {
+ mHeadsUpManager.setIsExpanded(isExpanded);
+ mStatusBar.setPanelExpanded(isExpanded);
+ mPanelExpanded = isExpanded;
+ }
+ }
+
/**
* @return a temporary override of {@link #mQsMaxExpansionHeight}, which is needed when
* collapsing QS / the panel when QS was scrolled
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 1361038..9fe591e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1967,6 +1967,10 @@
return !mUnlockMethodCache.isCurrentlyInsecure();
}
+ public void setPanelExpanded(boolean isExpanded) {
+ mStatusBarWindowManager.setPanelExpanded(isExpanded);
+ }
+
/**
* All changes to the status bar and notifications funnel through here and are batched.
*/
@@ -2027,7 +2031,7 @@
// Expand the window to encompass the full screen in anticipation of the drag.
// This is only possible to do atomically because the status bar is at the top of the screen!
- mStatusBarWindowManager.setStatusBarExpanded(true);
+ mStatusBarWindowManager.setPanelVisible(true);
mStatusBarView.setFocusable(false);
visibilityChanged(true);
@@ -2156,7 +2160,7 @@
visibilityChanged(false);
// Shrink the window to the size of the status bar only
- mStatusBarWindowManager.setStatusBarExpanded(false);
+ mStatusBarWindowManager.setPanelVisible(false);
mStatusBarWindowManager.setForceStatusBarVisible(false);
mStatusBarView.setFocusable(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index 422d868..4f1c652 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -114,12 +114,12 @@
}
private void applyFocusableFlag(State state) {
+ boolean panelFocusable = state.statusBarFocusable && state.panelExpanded;
if (state.isKeyguardShowingAndNotOccluded() && state.keyguardNeedsInput
- && state.bouncerShowing
- || BaseStatusBar.ENABLE_REMOTE_INPUT && state.statusBarExpanded) {
+ && state.bouncerShowing || BaseStatusBar.ENABLE_REMOTE_INPUT && panelFocusable) {
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- } else if (state.isKeyguardShowingAndNotOccluded() || state.statusBarFocusable) {
+ } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mLpChanged.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
} else {
@@ -130,7 +130,7 @@
private void applyHeight(State state) {
boolean expanded = !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
- || state.statusBarExpanded || state.keyguardFadingAway || state.bouncerShowing
+ || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
|| state.headsUpShowing);
if (expanded) {
mLpChanged.height = ViewGroup.LayoutParams.MATCH_PARENT;
@@ -213,9 +213,9 @@
apply(mCurrentState);
}
- public void setStatusBarExpanded(boolean expanded) {
- mCurrentState.statusBarExpanded = expanded;
- mCurrentState.statusBarFocusable = expanded;
+ public void setPanelVisible(boolean visible) {
+ mCurrentState.panelVisible = visible;
+ mCurrentState.statusBarFocusable = visible;
apply(mCurrentState);
}
@@ -267,11 +267,17 @@
apply(mCurrentState);
}
+ public void setPanelExpanded(boolean isExpanded) {
+ mCurrentState.panelExpanded = isExpanded;
+ apply(mCurrentState);
+ }
+
private static class State {
boolean keyguardShowing;
boolean keyguardOccluded;
boolean keyguardNeedsInput;
- boolean statusBarExpanded;
+ boolean panelVisible;
+ boolean panelExpanded;
boolean statusBarFocusable;
boolean bouncerShowing;
boolean keyguardFadingAway;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 114427c..ed98a159 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -59,6 +59,9 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("BluetoothController state:");
pw.print(" mLocalBluetoothManager="); pw.println(mLocalBluetoothManager);
+ if (mLocalBluetoothManager == null) {
+ return;
+ }
pw.print(" mEnabled="); pw.println(mEnabled);
pw.print(" mConnecting="); pw.println(mConnecting);
pw.print(" mLastDevice="); pw.println(mLastDevice);
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 23813d1..ad21555 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -148,6 +148,11 @@
android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
// Kick current state into place
+ final List<DiskInfo> disks = mStorageManager.getDisks();
+ for (DiskInfo disk : disks) {
+ onDiskScannedInternal(disk, disk.volumeCount);
+ }
+
final List<VolumeInfo> vols = mStorageManager.getVolumes();
for (VolumeInfo vol : vols) {
onVolumeStateChangedInternal(vol);
@@ -194,7 +199,7 @@
}
private void onDiskScannedInternal(DiskInfo disk, int volumeCount) {
- if (volumeCount == 0) {
+ if (volumeCount == 0 && disk.size > 0) {
// No supported volumes found, give user option to format
final CharSequence title = mContext.getString(
R.string.ext_media_unmountable_notification_title, disk.getDescription());
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 6c1023c..01cc2ca 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1725,28 +1725,68 @@
// At package-changed we only care about looking at new transport states
if (changed) {
try {
+ String[] components =
+ intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+
if (MORE_DEBUG) {
Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
+ for (int i = 0; i < components.length; i++) {
+ Slog.i(TAG, " * " + components[i]);
+ }
}
- // unbind existing possibly-stale connections to that package's transports
+
+ // In general we need to try to bind any time we see a component enable
+ // state change, because that change may have made a transport available.
+ // However, because we currently only support a single transport component
+ // per package, we can skip the bind attempt if the change (a) affects a
+ // package known to host a transport, but (b) does not affect the known
+ // transport component itself.
+ //
+ // In addition, if the change *is* to a known transport component, we need
+ // to unbind it before retrying the binding.
+ boolean tryBind = true;
synchronized (mTransports) {
TransportConnection conn = mTransportConnections.get(pkgName);
if (conn != null) {
+ // We have a bound transport in this package; do we need to rebind it?
final ServiceInfo svc = conn.mTransport;
ComponentName svcName =
new ComponentName(svc.packageName, svc.name);
- String flatName = svcName.flattenToShortString();
- Slog.i(TAG, "Unbinding " + svcName);
-
- mContext.unbindService(conn);
- mTransportConnections.remove(pkgName);
- mTransports.remove(mTransportNames.get(flatName));
- mTransportNames.remove(flatName);
+ if (svc.packageName.equals(pkgName)) {
+ final String className = svcName.getClassName();
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Checking need to rebind " + className);
+ }
+ // See whether it's the transport component within this package
+ boolean isTransport = false;
+ for (int i = 0; i < components.length; i++) {
+ if (className.equals(components[i])) {
+ // Okay, it's an existing transport component.
+ final String flatName = svcName.flattenToShortString();
+ mContext.unbindService(conn);
+ mTransportConnections.remove(pkgName);
+ mTransports.remove(mTransportNames.get(flatName));
+ mTransportNames.remove(flatName);
+ isTransport = true;
+ break;
+ }
+ }
+ if (!isTransport) {
+ // A non-transport component within a package that is hosting
+ // a bound transport
+ tryBind = false;
+ }
+ }
}
}
- // and then (re)bind as appropriate
- PackageInfo app = mPackageManager.getPackageInfo(pkgName, 0);
- checkForTransportAndBind(app);
+ // and now (re)bind as appropriate
+ if (tryBind) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Yes, need to recheck binding");
+ }
+ PackageInfo app = mPackageManager.getPackageInfo(pkgName, 0);
+ checkForTransportAndBind(app);
+ }
} catch (NameNotFoundException e) {
// Nope, can't find it - just ignore
if (MORE_DEBUG) {
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 7d427d6..79c66b9 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -408,11 +408,19 @@
}
}
if (repCbs != null) {
- for (int i=0; i<repCbs.size(); i++) {
- try {
- repCbs.get(i).mCallback.opChanged(code, packageName);
- } catch (RemoteException e) {
+ // There are components watching for mode changes such as window manager
+ // and location manager which are in our process. The callbacks in these
+ // components may require permissions our remote caller does not have.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ for (int i = 0; i < repCbs.size(); i++) {
+ try {
+ repCbs.get(i).mCallback.opChanged(code, packageName);
+ } catch (RemoteException e) {
+ }
}
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index c46fa76..66fd36f 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -205,6 +205,7 @@
// Clear registered LE apps to force shut-off
synchronized (this) {
mBleAppCount = 0;
+ mBleApps.clear();
}
if (st == BluetoothAdapter.STATE_BLE_ON) {
//if state is BLE_ON make sure you trigger disableBLE part
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index e00cf5b..d48953d 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -973,6 +973,7 @@
}
}
+ disk.volumeCount = volumeCount;
mCallbacks.notifyDiskScanned(disk, volumeCount);
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 62f168d..d214a20 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -2355,9 +2355,9 @@
callingUid = packageUid;
}
checkReadAccountsPermission();
- UserAccounts accounts = getUserAccounts(userId);
long identityToken = clearCallingIdentity();
try {
+ UserAccounts accounts = getUserAccounts(userId);
synchronized (accounts.cacheLock) {
return getAccountsFromCacheLocked(accounts, type, callingUid, callingPackage);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6842304..5a9b5b2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8884,7 +8884,8 @@
throw new SecurityException("updateLockTaskPackage called from non-system process");
}
synchronized (this) {
- if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" + packages);
+ if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" +
+ Arrays.toString(packages));
mLockTaskPackages.put(userId, packages);
mStackSupervisor.onLockTaskPackagesUpdatedLocked();
}
@@ -8927,7 +8928,7 @@
mStackSupervisor.setLockTaskModeLocked(task, isSystemInitiated ?
ActivityManager.LOCK_TASK_MODE_PINNED :
ActivityManager.LOCK_TASK_MODE_LOCKED,
- "startLockTask");
+ "startLockTask", true);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -8992,7 +8993,7 @@
// Stop lock task
synchronized (this) {
mStackSupervisor.setLockTaskModeLocked(null, ActivityManager.LOCK_TASK_MODE_NONE,
- "stopLockTask");
+ "stopLockTask", true);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -19433,7 +19434,7 @@
}
mStackSupervisor.setLockTaskModeLocked(null, ActivityManager.LOCK_TASK_MODE_NONE,
- "startUser");
+ "startUser", false);
final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId);
if (userInfo == null) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index f304828..5eee34f 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1185,7 +1185,7 @@
final TaskRecord task = r.task;
if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
- setLockTaskModeLocked(task, LOCK_TASK_MODE_LOCKED, "mLockTaskAuth==LAUNCHABLE");
+ setLockTaskModeLocked(task, LOCK_TASK_MODE_LOCKED, "mLockTaskAuth==LAUNCHABLE", false);
}
final ActivityStack stack = task.stack;
@@ -3675,7 +3675,11 @@
}
void removeLockedTaskLocked(final TaskRecord task) {
- if (mLockTaskModeTasks.remove(task) && mLockTaskModeTasks.isEmpty()) {
+ if (!mLockTaskModeTasks.remove(task)) {
+ return;
+ }
+ if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "removeLockedTaskLocked: removed " + task);
+ if (mLockTaskModeTasks.isEmpty()) {
// Last one.
if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: task=" + task +
" last task, reverting locktask mode. Callers=" + Debug.getCallers(3));
@@ -3696,7 +3700,8 @@
}
}
- void setLockTaskModeLocked(TaskRecord task, int lockTaskModeState, String reason) {
+ void setLockTaskModeLocked(TaskRecord task, int lockTaskModeState, String reason,
+ boolean andResume) {
if (task == null) {
// Take out of lock task mode if necessary
final TaskRecord lockedTask = getLockedTaskLocked();
@@ -3745,8 +3750,11 @@
if (task.mLockTaskUid == -1) {
task.mLockTaskUid = task.mCallingUid;
}
- findTaskToMoveToFrontLocked(task, 0, null, reason);
- resumeTopActivitiesLocked();
+
+ if (andResume) {
+ findTaskToMoveToFrontLocked(task, 0, null, reason);
+ resumeTopActivitiesLocked();
+ }
}
boolean isLockTaskModeViolation(TaskRecord task) {
@@ -3780,6 +3788,8 @@
lockedTask.setLockTaskAuth();
if (wasLaunchable && lockedTask.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE) {
// Lost whitelisting authorization. End it now.
+ if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " +
+ lockedTask + " mLockTaskAuth=" + lockedTask.lockTaskAuthToString());
removeLockedTaskLocked(lockedTask);
lockedTask.performClearTaskLocked();
didSomething = true;
@@ -3797,7 +3807,11 @@
if (mLockTaskModeTasks.isEmpty() && task != null
&& task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
// This task must have just been authorized.
- setLockTaskModeLocked(task, ActivityManager.LOCK_TASK_MODE_LOCKED, "package updated");
+ if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK,
+ "onLockTaskPackagesUpdated: starting new locktask task=" + task);
+ setLockTaskModeLocked(task, ActivityManager.LOCK_TASK_MODE_LOCKED, "package updated",
+ false);
+ didSomething = true;
}
if (didSomething) {
resumeTopActivitiesLocked();
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 3a20ded..9f11def 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -435,9 +435,7 @@
*/
int trimForTaskLocked(TaskRecord task, boolean doTrim) {
int recentsCount = size();
- final Intent intent = task.intent;
- final boolean document = intent != null && intent.isDocument();
-
+ final boolean document = task.intent != null && task.intent.isDocument();
int maxRecents = task.maxRecents - 1;
for (int i = 0; i < recentsCount; i++) {
final TaskRecord tr = get(i);
@@ -448,12 +446,11 @@
if (i > MAX_RECENT_BITMAPS) {
tr.freeLastThumbnail();
}
- final Intent trIntent = tr.intent;
- if ((task.affinity == null || !task.affinity.equals(tr.affinity)) &&
- (intent == null || !intent.filterEquals(trIntent))) {
+ if (task.realActivity == null || tr.realActivity == null ||
+ !task.realActivity.equals(tr.realActivity)) {
continue;
}
- final boolean trIsDocument = trIntent != null && trIntent.isDocument();
+ final boolean trIsDocument = tr.intent != null && tr.intent.isDocument();
if (document && trIsDocument) {
// These are the same document activity (not necessarily the same doc).
if (maxRecents > 0) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 417c7c3..d56a024 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -739,38 +739,40 @@
performClearTaskAtIndexLocked(0);
}
+ String lockTaskAuthToString() {
+ switch (mLockTaskAuth) {
+ case LOCK_TASK_AUTH_DONT_LOCK: return "LOCK_TASK_AUTH_DONT_LOCK";
+ case LOCK_TASK_AUTH_PINNABLE: return "LOCK_TASK_AUTH_PINNABLE";
+ case LOCK_TASK_AUTH_LAUNCHABLE: return "LOCK_TASK_AUTH_LAUNCHABLE";
+ case LOCK_TASK_AUTH_WHITELISTED: return "LOCK_TASK_AUTH_WHITELISTED";
+ default: return "unknown=" + mLockTaskAuth;
+ }
+ }
+
void setLockTaskAuth() {
switch (mLockTaskMode) {
case LOCK_TASK_LAUNCH_MODE_DEFAULT:
- if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "setLockTaskAuth: task=" + this +
- " mLockTaskAuth=" + (isLockTaskWhitelistedLocked() ?
- "WHITELISTED" : "PINNABLE"));
mLockTaskAuth = isLockTaskWhitelistedLocked() ?
LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
break;
case LOCK_TASK_LAUNCH_MODE_NEVER:
- if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "setLockTaskAuth: task=" + this +
- " mLockTaskAuth=" + (mPrivileged ? "DONT_LOCK" : "PINNABLE"));
mLockTaskAuth = mPrivileged ?
LOCK_TASK_AUTH_DONT_LOCK : LOCK_TASK_AUTH_PINNABLE;
break;
case LOCK_TASK_LAUNCH_MODE_ALWAYS:
- if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "setLockTaskAuth: task=" + this +
- " mLockTaskAuth=" + (mPrivileged ? "LAUNCHABLE" : "PINNABLE"));
mLockTaskAuth = mPrivileged ?
LOCK_TASK_AUTH_LAUNCHABLE: LOCK_TASK_AUTH_PINNABLE;
break;
case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
- if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "setLockTaskAuth: task=" + this +
- " mLockTaskAuth=" + (isLockTaskWhitelistedLocked() ?
- "LAUNCHABLE" : "PINNABLE"));
mLockTaskAuth = isLockTaskWhitelistedLocked() ?
LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
break;
}
+ if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "setLockTaskAuth: task=" + this +
+ " mLockTaskAuth=" + lockTaskAuthToString());
}
boolean isLockTaskWhitelistedLocked() {
@@ -1173,10 +1175,12 @@
pw.print(" taskType="); pw.print(taskType);
pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo);
}
- if (rootWasReset || mNeverRelinquishIdentity || mReuseTask) {
+ if (rootWasReset || mNeverRelinquishIdentity || mReuseTask
+ || mLockTaskAuth != LOCK_TASK_AUTH_PINNABLE) {
pw.print(prefix); pw.print("rootWasReset="); pw.print(rootWasReset);
pw.print(" mNeverRelinquishIdentity="); pw.print(mNeverRelinquishIdentity);
- pw.print(" mReuseTask="); pw.println(mReuseTask);
+ pw.print(" mReuseTask="); pw.print(mReuseTask);
+ pw.print(" mLockTaskAuth="); pw.println(lockTaskAuthToString());
}
if (mAffiliatedTaskId != taskId || mPrevAffiliateTaskId != INVALID_TASK_ID
|| mPrevAffiliate != null || mNextAffiliateTaskId != INVALID_TASK_ID
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9f35843..352c499 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4787,7 +4787,7 @@
if (devicePort.type() == AudioManager.DEVICE_OUT_HDMI ||
devicePort.type() == AudioManager.DEVICE_OUT_HDMI_ARC) {
// format the list of supported encodings
- int[] formats = devicePort.formats();
+ int[] formats = AudioFormat.filterPublicFormats(devicePort.formats());
if (formats.length > 0) {
ArrayList<Integer> encodingList = new ArrayList(1);
for (int format : formats) {
diff --git a/services/core/java/com/android/server/camera/CameraService.java b/services/core/java/com/android/server/camera/CameraService.java
index 1d77bc2..777a9dd 100644
--- a/services/core/java/com/android/server/camera/CameraService.java
+++ b/services/core/java/com/android/server/camera/CameraService.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.ICameraService;
+import android.hardware.ICameraServiceProxy;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserManager;
@@ -42,14 +43,30 @@
*/
private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
+ public static final String CAMERA_SERVICE_PROXY_BINDER_NAME = "media.camera.proxy";
+
// Event arguments to use with the camera service notifySystemEvent call:
public static final int NO_EVENT = 0; // NOOP
public static final int USER_SWITCHED = 1; // User changed, argument is the new user handle
private final Context mContext;
private UserManager mUserManager;
+
+ private final Object mLock = new Object();
private Set<Integer> mEnabledCameraUsers;
+ private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() {
+ @Override
+ public void pingForUserUpdate() {
+ // Binder call
+ synchronized(mLock) {
+ if (mEnabledCameraUsers != null) {
+ notifyMediaserver(USER_SWITCHED, mEnabledCameraUsers);
+ }
+ }
+ }
+ };
+
public CameraService(Context context) {
super(context);
mContext = context;
@@ -62,18 +79,27 @@
// Should never see this unless someone messes up the SystemServer service boot order.
throw new IllegalStateException("UserManagerService must start before CameraService!");
}
+ publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy);
}
@Override
public void onStartUser(int userHandle) {
- if (mEnabledCameraUsers == null) {
- // Initialize mediaserver, or update mediaserver if we are recovering from a crash.
- onSwitchUser(userHandle);
+ synchronized(mLock) {
+ if (mEnabledCameraUsers == null) {
+ // Initialize mediaserver, or update mediaserver if we are recovering from a crash.
+ switchUserLocked(userHandle);
+ }
}
}
@Override
public void onSwitchUser(int userHandle) {
+ synchronized(mLock) {
+ switchUserLocked(userHandle);
+ }
+ }
+
+ private void switchUserLocked(int userHandle) {
Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle);
if (mEnabledCameraUsers == null || !mEnabledCameraUsers.equals(currentUserHandles)) {
// Some user handles have been added or removed, update mediaserver.
@@ -82,7 +108,6 @@
}
}
-
private Set<Integer> getEnabledUserHandles(int currentUserHandle) {
List<UserInfo> userProfiles = mUserManager.getEnabledProfiles(currentUserHandle);
Set<Integer> handles = new HashSet<>(userProfiles.size());
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index fba9258..bf896a5 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -190,10 +190,8 @@
private int mReevaluateToken = 0;
private static final int INVALID_UID = -1;
private int mUidResponsibleForReeval = INVALID_UID;
- // When network has been evaluated this many times:
- // 1. report NETWORK_TEST_RESULT_INVALID
- // 2. stop blaming UID that requested re-evaluation for further attempts
- private static final int INITIAL_EVALUATION_ATTEMPTS = 3;
+ // Stop blaming UID that requested re-evaluation after this many attempts.
+ private static final int BLAME_FOR_EVALUATION_ATTEMPTS = 5;
private final Context mContext;
private final Handler mConnectivityServiceHandler;
@@ -438,12 +436,12 @@
} else if (httpResponseCode >= 200 && httpResponseCode <= 399) {
transitionTo(mCaptivePortalState);
} else {
- Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
+ final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
sendMessageDelayed(msg, mReevaluateDelayMs);
- if (mAttempts >= INITIAL_EVALUATION_ATTEMPTS) {
- mConnectivityServiceHandler.sendMessage(obtainMessage(
- EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, 0,
- mNetworkAgentInfo));
+ mConnectivityServiceHandler.sendMessage(obtainMessage(
+ EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, 0,
+ mNetworkAgentInfo));
+ if (mAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
// Don't continue to blame UID forever.
TrafficStats.clearThreadStatsUid();
}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index d725d94..1057ce3 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -30,6 +30,7 @@
import android.os.Looper;
import android.os.MessageQueue;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Slog;
import com.android.server.SystemService;
@@ -37,6 +38,8 @@
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintService;
+import android.hardware.fingerprint.IFingerprintDaemon;
+import android.hardware.fingerprint.IFingerprintDaemonCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
@@ -55,20 +58,19 @@
*
* @hide
*/
-public class FingerprintService extends SystemService {
+public class FingerprintService extends SystemService implements IBinder.DeathRecipient {
private static final String TAG = "FingerprintService";
private static final boolean DEBUG = true;
+ private static final String FP_DATA_DIR = "fpdata";
+ private static final String FINGERPRINTD = "android.hardware.fingerprint.IFingerprintDaemon";
+ private static final int MSG_USER_SWITCHING = 10;
+ private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
+
private ClientMonitor mAuthClient = null;
private ClientMonitor mEnrollClient = null;
private ClientMonitor mRemoveClient = null;
-
private final AppOpsManager mAppOps;
- private static final int MSG_NOTIFY = 10;
- private static final int MSG_USER_SWITCHING = 11;
-
- private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
-
// Message types. Used internally to dispatch messages to the correct callback.
// Must agree with the list in fingerprint.h
private static final int FINGERPRINT_ERROR = -1;
@@ -83,11 +85,6 @@
Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
- case MSG_NOTIFY:
- FpHalMsg m = (FpHalMsg) msg.obj;
- handleNotify(m.type, m.arg1, m.arg2, m.arg3);
- break;
-
case MSG_USER_SWITCHING:
handleUserSwitching(msg.arg1);
break;
@@ -97,10 +94,13 @@
}
}
};
- private Context mContext;
- private int mHalDeviceId;
- private int mFailedAttempts;
+
private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance();
+ private Context mContext;
+ private long mHalDeviceId;
+ private int mFailedAttempts;
+ private IFingerprintDaemon mDaemon;
+
private final Runnable mLockoutReset = new Runnable() {
@Override
public void run() {
@@ -112,127 +112,115 @@
super(context);
mContext = context;
mAppOps = context.getSystemService(AppOpsManager.class);
- nativeInit(Looper.getMainLooper().getQueue(), this);
}
- // TODO: Move these into separate process
- // JNI methods to communicate from FingerprintService to HAL
- static native int nativeEnroll(byte [] token, int groupId, int timeout);
- static native long nativePreEnroll();
- static native int nativeStopEnrollment();
- static native int nativeAuthenticate(long sessionId, int groupId);
- static native int nativeStopAuthentication();
- static native int nativeRemove(int fingerId, int groupId);
- static native int nativeOpenHal();
- static native int nativeCloseHal();
- static native void nativeInit(MessageQueue queue, FingerprintService service);
- static native long nativeGetAuthenticatorId();
- static native int nativeSetActiveGroup(int gid, byte[] storePath);
-
- static final class FpHalMsg {
- int type; // Type of the message. One of the constants in fingerprint.h
- int arg1; // optional arguments
- int arg2;
- int arg3;
-
- FpHalMsg(int type, int arg1, int arg2, int arg3) {
- this.type = type;
- this.arg1 = arg1;
- this.arg2 = arg2;
- this.arg3 = arg3;
- }
+ @Override
+ public void binderDied() {
+ Slog.v(TAG, "fingerprintd died");
+ mDaemon = null;
}
- /**
- * Called from JNI to communicate messages from fingerprint HAL.
- */
- void notify(int type, int arg1, int arg2, int arg3) {
- mHandler.obtainMessage(MSG_NOTIFY, new FpHalMsg(type, arg1, arg2, arg3)).sendToTarget();
- }
-
- void handleNotify(int type, int arg1, int arg2, int arg3) {
- Slog.v(TAG, "handleNotify(type=" + type + ", arg1=" + arg1 + ", arg2=" + arg2 + ")"
- + ", mAuthClients = " + mAuthClient + ", mEnrollClient = " + mEnrollClient);
- if (mEnrollClient != null) {
- final IBinder token = mEnrollClient.token;
- if (dispatchNotify(mEnrollClient, type, arg1, arg2, arg3)) {
- stopEnrollment(token, false);
- removeClient(mEnrollClient);
+ public IFingerprintDaemon getFingerprintDaemon() {
+ if (mDaemon == null) {
+ mDaemon = IFingerprintDaemon.Stub.asInterface(ServiceManager.getService(FINGERPRINTD));
+ if (mDaemon == null) {
+ Slog.w(TAG, "fingerprind service not available");
+ } else {
+ try {
+ mDaemon.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
+ mDaemon = null; // try again!
+ }
}
}
+ return mDaemon;
+ }
+
+ protected void dispatchEnumerate(long deviceId, int[] fingerIds, int[] groupIds) {
+ if (fingerIds.length != groupIds.length) {
+ Slog.w(TAG, "fingerIds and groupIds differ in length: f[]="
+ + fingerIds + ", g[]=" + groupIds);
+ return;
+ }
+ if (DEBUG) Slog.w(TAG, "Enumerate: f[]=" + fingerIds + ", g[]=" + groupIds);
+ // TODO: update fingerprint/name pairs
+ }
+
+ protected void dispatchRemoved(long deviceId, int fingerId, int groupId) {
+ final ClientMonitor client = mRemoveClient;
+ if (fingerId != 0) {
+ ContentResolver res = mContext.getContentResolver();
+ removeTemplateForUser(mRemoveClient, fingerId);
+ }
+ if (client != null && client.sendRemoved(fingerId, groupId)) {
+ removeClient(mRemoveClient);
+ }
+ }
+
+ protected void dispatchError(long deviceId, int error) {
+ if (mEnrollClient != null) {
+ final IBinder token = mEnrollClient.token;
+ if (mEnrollClient.sendError(error)) {
+ stopEnrollment(token, false);
+ }
+ } else if (mAuthClient != null) {
+ final IBinder token = mAuthClient.token;
+ if (mAuthClient.sendError(error)) {
+ stopAuthentication(token, false);
+ }
+ } else if (mRemoveClient != null) {
+ if (mRemoveClient.sendError(error)) removeClient(mRemoveClient);
+ }
+ }
+
+ protected void dispatchAuthenticated(long deviceId, int fingerId, int groupId) {
if (mAuthClient != null) {
final IBinder token = mAuthClient.token;
- if (dispatchNotify(mAuthClient, type, arg1, arg2, arg3)) {
+ if (mAuthClient.sendAuthenticated(fingerId, groupId)) {
stopAuthentication(token, false);
removeClient(mAuthClient);
}
}
- if (mRemoveClient != null) {
- if (dispatchNotify(mRemoveClient, type, arg1, arg2, arg3)) {
- removeClient(mRemoveClient);
+ }
+
+ protected void dispatchAcquired(long deviceId, int acquiredInfo) {
+ if (mEnrollClient != null) {
+ if (mEnrollClient.sendAcquired(acquiredInfo)) {
+ removeClient(mEnrollClient);
+ }
+ } else if (mAuthClient != null) {
+ if (mAuthClient.sendAcquired(acquiredInfo)) {
+ removeClient(mAuthClient);
}
}
+
}
void handleUserSwitching(int userId) {
updateActiveGroup(userId);
}
- /*
- * Dispatch notify events to clients.
- *
- * @return true if the operation is done, i.e. authentication completed
- */
- boolean dispatchNotify(ClientMonitor clientMonitor, int type, int arg1, int arg2, int arg3) {
- boolean operationCompleted = false;
- int fpId;
- int groupId;
- int remaining;
- int acquireInfo;
- switch (type) {
- case FINGERPRINT_ERROR:
- fpId = arg1;
- operationCompleted = clientMonitor.sendError(fpId);
- break;
- case FINGERPRINT_ACQUIRED:
- acquireInfo = arg1;
- operationCompleted = clientMonitor.sendAcquired(acquireInfo);
- break;
- case FINGERPRINT_AUTHENTICATED:
- fpId = arg1;
- groupId = arg2;
- operationCompleted = clientMonitor.sendAuthenticated(fpId, groupId);
- break;
- case FINGERPRINT_TEMPLATE_ENROLLING:
- fpId = arg1;
- groupId = arg2;
- remaining = arg3;
- operationCompleted = clientMonitor.sendEnrollResult(fpId, groupId, remaining);
+ protected void dispatchEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
+ if (mEnrollClient != null) {
+ if (mEnrollClient.sendEnrollResult(fingerId, groupId, remaining)) {
if (remaining == 0) {
- addTemplateForUser(clientMonitor, fpId);
- operationCompleted = true; // enroll completed
+ ContentResolver res = mContext.getContentResolver();
+ addTemplateForUser(mEnrollClient, fingerId);
+ removeClient(mEnrollClient);
}
- break;
- case FINGERPRINT_TEMPLATE_REMOVED:
- fpId = arg1;
- groupId = arg2;
- operationCompleted = clientMonitor.sendRemoved(fpId, groupId);
- if (fpId != 0) {
- removeTemplateForUser(clientMonitor, fpId);
- }
- break;
+ }
}
- return operationCompleted;
}
- private void removeClient(ClientMonitor clientMonitor) {
- if (clientMonitor == null) return;
- clientMonitor.destroy();
- if (clientMonitor == mAuthClient) {
+ private void removeClient(ClientMonitor client) {
+ if (client == null) return;
+ client.destroy();
+ if (client == mAuthClient) {
mAuthClient = null;
- } else if (clientMonitor == mEnrollClient) {
+ } else if (client == mEnrollClient) {
mEnrollClient = null;
- } else if (clientMonitor == mRemoveClient) {
+ } else if (client == mRemoveClient) {
mRemoveClient = null;
}
}
@@ -273,17 +261,36 @@
void startEnrollment(IBinder token, byte[] cryptoToken, int groupId,
IFingerprintServiceReceiver receiver, int flags) {
+ IFingerprintDaemon daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "enroll: no fingeprintd!");
+ return;
+ }
stopPendingOperations();
mEnrollClient = new ClientMonitor(token, receiver, groupId);
final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
- final int result = nativeEnroll(cryptoToken, groupId, timeout);
- if (result != 0) {
- Slog.w(TAG, "startEnroll failed, result=" + result);
+ try {
+ final int result = daemon.enroll(cryptoToken, groupId, timeout);
+ if (result != 0) {
+ Slog.w(TAG, "startEnroll failed, result=" + result);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "startEnroll failed", e);
}
}
public long startPreEnroll(IBinder token) {
- return nativePreEnroll();
+ IFingerprintDaemon daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "startPreEnroll: no fingeprintd!");
+ return 0;
+ }
+ try {
+ return daemon.preEnroll();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "startPreEnroll failed", e);
+ }
+ return 0;
}
private void stopPendingOperations() {
@@ -297,20 +304,34 @@
}
void stopEnrollment(IBinder token, boolean notify) {
+ IFingerprintDaemon daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "stopEnrollment: no fingeprintd!");
+ return;
+ }
final ClientMonitor client = mEnrollClient;
if (client == null || client.token != token) return;
- int result = nativeStopEnrollment();
+ try {
+ int result = daemon.cancelEnrollment();
+ if (result != 0) {
+ Slog.w(TAG, "startEnrollCancel failed, result = " + result);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "stopEnrollment failed", e);
+ }
if (notify) {
client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
}
removeClient(mEnrollClient);
- if (result != 0) {
- Slog.w(TAG, "startEnrollCancel failed, result=" + result);
- }
}
void startAuthentication(IBinder token, long opId, int groupId,
IFingerprintServiceReceiver receiver, int flags) {
+ IFingerprintDaemon daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "startAuthentication: no fingeprintd!");
+ return;
+ }
stopPendingOperations();
mAuthClient = new ClientMonitor(token, receiver, groupId);
if (inLockoutMode()) {
@@ -322,32 +343,54 @@
return;
}
final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
- final int result = nativeAuthenticate(opId, groupId);
- if (result != 0) {
- Slog.w(TAG, "startAuthentication failed, result=" + result);
+ try {
+ final int result = daemon.authenticate(opId, groupId);
+ if (result != 0) {
+ Slog.w(TAG, "startAuthentication failed, result=" + result);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "startAuthentication failed", e);
}
}
void stopAuthentication(IBinder token, boolean notify) {
+ IFingerprintDaemon daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "stopAuthentication: no fingeprintd!");
+ return;
+ }
final ClientMonitor client = mAuthClient;
if (client == null || client.token != token) return;
- int result = nativeStopAuthentication();
+ try {
+ int result = daemon.cancelAuthentication();
+ if (result != 0) {
+ Slog.w(TAG, "stopAuthentication failed, result=" + result);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "stopAuthentication failed", e);
+ }
if (notify) {
client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
}
removeClient(mAuthClient);
- if (result != 0) {
- Slog.w(TAG, "stopAuthentication failed, result=" + result);
- }
}
void startRemove(IBinder token, int fingerId, int userId,
IFingerprintServiceReceiver receiver) {
+ IFingerprintDaemon daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "startRemove: no fingeprintd!");
+ return;
+ }
mRemoveClient = new ClientMonitor(token, receiver, userId);
// The fingerprint template ids will be removed when we get confirmation from the HAL
- final int result = nativeRemove(fingerId, userId);
- if (result != 0) {
- Slog.w(TAG, "startRemove with id = " + fingerId + " failed with result=" + result);
+ try {
+ final int result = daemon.remove(fingerId, userId);
+ if (result != 0) {
+ Slog.w(TAG, "startRemove with id = " + fingerId + " failed, result=" + result);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "startRemove failed", e);
}
}
@@ -364,7 +407,7 @@
"Must have " + permission + " permission.");
}
- private boolean canUserFingerPrint(String opPackageName) {
+ private boolean canUseFingerprint(String opPackageName) {
checkPermission(USE_FINGERPRINT);
return mAppOps.noteOp(AppOpsManager.OP_USE_FINGERPRINT, Binder.getCallingUid(),
@@ -496,15 +539,48 @@
}
}
- private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
+ private IFingerprintDaemonCallback mDaemonCallback = new IFingerprintDaemonCallback.Stub() {
+
@Override
+ public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
+ dispatchEnrollResult(deviceId, fingerId, groupId, remaining);
+ }
+
+ @Override
+ public void onAcquired(long deviceId, int acquiredInfo) {
+ dispatchAcquired(deviceId, acquiredInfo);
+ }
+
+ @Override
+ public void onAuthenticated(long deviceId, int fingerId, int groupId) {
+ dispatchAuthenticated(deviceId, fingerId, groupId);
+ }
+
+ @Override
+ public void onError(long deviceId, int error) {
+ dispatchError(deviceId, error);
+ }
+
+ @Override
+ public void onRemoved(long deviceId, int fingerId, int groupId) {
+ dispatchRemoved(deviceId, fingerId, groupId);
+ }
+
+ @Override
+ public void onEnumerate(long deviceId, int[] fingerIds, int[] groupIds) {
+ dispatchEnumerate(deviceId, fingerIds, groupIds);
+ }
+
+ };
+
+ private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
+ @Override // Binder call
public long preEnroll(IBinder token) {
checkPermission(MANAGE_FINGERPRINT);
return startPreEnroll(token);
}
- @Override
- // Binder call
+ @Override // Binder call
public void enroll(final IBinder token, final byte[] cryptoToken, final int groupId,
final IFingerprintServiceReceiver receiver, final int flags) {
checkPermission(MANAGE_FINGERPRINT);
@@ -517,8 +593,7 @@
});
}
- @Override
- // Binder call
+ @Override // Binder call
public void cancelEnrollment(final IBinder token) {
checkPermission(MANAGE_FINGERPRINT);
mHandler.post(new Runnable() {
@@ -529,12 +604,11 @@
});
}
- @Override
- // Binder call
+ @Override // Binder call
public void authenticate(final IBinder token, final long opId, final int groupId,
final IFingerprintServiceReceiver receiver, final int flags, String opPackageName) {
checkPermission(USE_FINGERPRINT);
- if (!canUserFingerPrint(opPackageName)) {
+ if (!canUseFingerprint(opPackageName)) {
return;
}
mHandler.post(new Runnable() {
@@ -545,11 +619,9 @@
});
}
- @Override
-
- // Binder call
+ @Override // Binder call
public void cancelAuthentication(final IBinder token, String opPackageName) {
- if (!canUserFingerPrint(opPackageName)) {
+ if (!canUseFingerprint(opPackageName)) {
return;
}
mHandler.post(new Runnable() {
@@ -560,8 +632,7 @@
});
}
- @Override
- // Binder call
+ @Override // Binder call
public void remove(final IBinder token, final int fingerId, final int groupId,
final IFingerprintServiceReceiver receiver) {
checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
@@ -574,17 +645,15 @@
}
- @Override
- // Binder call
+ @Override // Binder call
public boolean isHardwareDetected(long deviceId, String opPackageName) {
- if (!canUserFingerPrint(opPackageName)) {
+ if (!canUseFingerprint(opPackageName)) {
return false;
}
- return mHalDeviceId != 0; // TODO
+ return mHalDeviceId != 0;
}
- @Override
- // Binder call
+ @Override // Binder call
public void rename(final int fingerId, final int groupId, final String name) {
checkPermission(MANAGE_FINGERPRINT);
mHandler.post(new Runnable() {
@@ -595,69 +664,102 @@
});
}
- @Override
- // Binder call
+ @Override // Binder call
public List<Fingerprint> getEnrolledFingerprints(int groupId, String opPackageName) {
- if (!canUserFingerPrint(opPackageName)) {
+ if (!canUseFingerprint(opPackageName)) {
return Collections.emptyList();
}
return FingerprintService.this.getEnrolledFingerprints(groupId);
}
- @Override
- // Binder call
+ @Override // Binder call
public boolean hasEnrolledFingerprints(int groupId, String opPackageName) {
- if (!canUserFingerPrint(opPackageName)) {
+ if (!canUseFingerprint(opPackageName)) {
return false;
}
return FingerprintService.this.hasEnrolledFingerprints(groupId);
}
- @Override
+ @Override // Binder call
public long getAuthenticatorId(String opPackageName) {
- if (!canUserFingerPrint(opPackageName)) {
+ if (!canUseFingerprint(opPackageName)) {
return 0;
}
- return nativeGetAuthenticatorId();
+ return FingerprintService.this.getAuthenticatorId();
}
}
@Override
public void onStart() {
publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
- mHalDeviceId = nativeOpenHal();
- updateActiveGroup(ActivityManager.getCurrentUser());
+ IFingerprintDaemon daemon = getFingerprintDaemon();
+ if (daemon != null) {
+ try {
+ daemon.init(mDaemonCallback);
+ mHalDeviceId = daemon.openHal();
+ updateActiveGroup(ActivityManager.getCurrentUser());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to open fingeprintd HAL", e);
+ }
+ }
if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
listenForUserSwitches();
}
private void updateActiveGroup(int userId) {
- if (mHalDeviceId != 0) {
- File path = Environment.getUserSystemDirectory(userId);
- nativeSetActiveGroup(userId, path.getAbsolutePath().getBytes());
+ IFingerprintDaemon daemon = getFingerprintDaemon();
+ if (daemon != null) {
+ try {
+ // TODO: if this is a managed profile, use the profile parent's directory for
+ // storage.
+ final File systemDir = Environment.getUserSystemDirectory(userId);
+ final File fpDir = new File(systemDir, FP_DATA_DIR);
+ if (!fpDir.exists()) {
+ if (!fpDir.mkdir()) {
+ Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath());
+ return;
+ }
+ }
+ daemon.setActiveGroup(userId, fpDir.getAbsolutePath().getBytes());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to setActiveGroup():", e);
+ }
}
}
private void listenForUserSwitches() {
try {
ActivityManagerNative.getDefault().registerUserSwitchObserver(
- new IUserSwitchObserver.Stub() {
- @Override
- public void onUserSwitching(int newUserId, IRemoteCallback reply) {
- mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
- .sendToTarget();
- }
- @Override
- public void onUserSwitchComplete(int newUserId) throws RemoteException {
- // Ignore.
- }
- @Override
- public void onForegroundProfileSwitch(int newProfileId) {
- // Ignore.
- }
- });
+ new IUserSwitchObserver.Stub() {
+ @Override
+ public void onUserSwitching(int newUserId, IRemoteCallback reply) {
+ mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
+ .sendToTarget();
+ }
+ @Override
+ public void onUserSwitchComplete(int newUserId) throws RemoteException {
+ // Ignore.
+ }
+ @Override
+ public void onForegroundProfileSwitch(int newProfileId) {
+ // Ignore.
+ }
+ });
} catch (RemoteException e) {
Slog.w(TAG, "Failed to listen for user switching event" ,e);
}
}
+
+ public long getAuthenticatorId() {
+ IFingerprintDaemon daemon = getFingerprintDaemon();
+ if (daemon != null) {
+ try {
+ return daemon.getAuthenticatorId();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "getAuthenticatorId failed", e);
+ }
+ }
+ return 0;
+ }
+
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0ecdd61..6a47238 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -369,11 +369,14 @@
/** Permission grant: grant the permission as an install permission. */
private static final int GRANT_INSTALL = 2;
+ /** Permission grant: grant the permission as an install permission for a legacy app. */
+ private static final int GRANT_INSTALL_LEGACY = 3;
+
/** Permission grant: grant the permission as a runtime one. */
- private static final int GRANT_RUNTIME = 3;
+ private static final int GRANT_RUNTIME = 4;
/** Permission grant: grant as runtime a permission that was granted as an install time one. */
- private static final int GRANT_UPGRADE = 4;
+ private static final int GRANT_UPGRADE = 5;
final ServiceThread mHandlerThread;
@@ -3180,6 +3183,12 @@
final PermissionsState permissionsState = sb.getPermissionsState();
+ final int flags = permissionsState.getPermissionFlags(name, userId);
+ if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
+ throw new SecurityException("Cannot grant system fixed permission: "
+ + name + " for package: " + packageName);
+ }
+
final int result = permissionsState.grantRuntimePermission(bp, userId);
switch (result) {
case PermissionsState.PERMISSION_OPERATION_FAILURE: {
@@ -3237,6 +3246,12 @@
final PermissionsState permissionsState = sb.getPermissionsState();
+ final int flags = permissionsState.getPermissionFlags(name, userId);
+ if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
+ throw new SecurityException("Cannot revoke system fixed permission: "
+ + name + " for package: " + packageName);
+ }
+
if (permissionsState.revokeRuntimePermission(bp, userId) ==
PermissionsState.PERMISSION_OPERATION_FAILURE) {
return;
@@ -4133,6 +4148,7 @@
final int userId = UserHandle.getCallingUserId();
ArrayList<ResolveInfo> result = new ArrayList<ResolveInfo>();
+ ArrayList<ResolveInfo> alwaysList = new ArrayList<ResolveInfo>();
ArrayList<ResolveInfo> undefinedList = new ArrayList<ResolveInfo>();
ArrayList<ResolveInfo> neverList = new ArrayList<ResolveInfo>();
ArrayList<ResolveInfo> matchAllList = new ArrayList<ResolveInfo>();
@@ -4155,27 +4171,22 @@
// Try to get the status from User settings first
int status = getDomainVerificationStatusLPr(ps, userId);
if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
- result.add(info);
+ alwaysList.add(info);
} else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
neverList.add(info);
- } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+ } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED ||
+ status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK) {
undefinedList.add(info);
}
}
}
- // Add all undefined Apps as we want them to appear in the Disambiguation dialog.
- result.addAll(undefinedList);
- // If there is nothing selected, add all candidates and remove the ones that the User
- // has explicitely put into the INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER state and
- // also remove Browser Apps ones.
- // If there is still none after this pass, add all Browser Apps and
- // let the User decide with the Disambiguation dialog if there are several ones.
- if (result.size() == 0) {
- result.addAll(candidates);
- }
- result.removeAll(neverList);
- result.removeAll(matchAllList);
- if (result.size() == 0) {
+ // First try to add the "always" if there is any
+ if (alwaysList.size() > 0) {
+ result.addAll(alwaysList);
+ } else {
+ // Add all undefined Apps as we want them to appear in the Disambiguation dialog.
+ result.addAll(undefinedList);
+ // Also add Browsers (all of them or only the default one)
if ((flags & MATCH_ALL) != 0) {
result.addAll(matchAllList);
} else {
@@ -4200,6 +4211,13 @@
result.addAll(matchAllList);
}
}
+
+ // If there is nothing selected, add all candidates and remove the ones that the User
+ // has explicitely put into the INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER state
+ if (result.size() == 0) {
+ result.addAll(candidates);
+ result.removeAll(neverList);
+ }
}
}
if (DEBUG_PREFERRED) {
@@ -6338,12 +6356,14 @@
// to scan the package again.
deriveNonSystemPackageAbi(pkg, scanFile, cpuAbiOverride, false /* extract libs */);
if (!TextUtils.equals(oldPrimaryCpuAbi, pkg.applicationInfo.primaryCpuAbi)) {
- throw new IllegalStateException("unexpected abi change for " + pkg.packageName + " ("
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "unexpected abi change for " + pkg.packageName + " ("
+ oldPrimaryCpuAbi + "-> " + pkg.applicationInfo.primaryCpuAbi);
}
if (!TextUtils.equals(oldSecondaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi)) {
- throw new IllegalStateException("unexpected abi change for " + pkg.packageName + " ("
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "unexpected abi change for " + pkg.packageName + " ("
+ oldSecondaryCpuAbi + "-> " + pkg.applicationInfo.secondaryCpuAbi);
}
}
@@ -7738,7 +7758,7 @@
case PermissionInfo.PROTECTION_DANGEROUS: {
if (pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) {
// For legacy apps dangerous permissions are install time ones.
- grant = GRANT_INSTALL;
+ grant = GRANT_INSTALL_LEGACY;
} else if (ps.isSystem()) {
final int[] updatedUserIds = ps.getPermissionsUpdatedForUserIds();
if (origPermissions.hasInstallPermission(bp.name)) {
@@ -7798,6 +7818,28 @@
switch (grant) {
case GRANT_INSTALL: {
+ // Revoke this as runtime permission to handle the case of
+ // a runtime permssion being downgraded to an install one.
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ if (origPermissions.getRuntimePermissionState(
+ bp.name, userId) != null) {
+ // Revoke the runtime permission and clear the flags.
+ origPermissions.revokeRuntimePermission(bp, userId);
+ origPermissions.updatePermissionFlags(bp, userId,
+ PackageManager.MASK_PERMISSION_FLAGS, 0);
+ // If we revoked a permission permission, we have to write.
+ changedRuntimePermissionUserIds = ArrayUtils.appendInt(
+ changedRuntimePermissionUserIds, userId);
+ }
+ }
+ // Grant an install permission.
+ if (permissionsState.grantInstallPermission(bp) !=
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ changedInstallPermission = true;
+ }
+ } break;
+
+ case GRANT_INSTALL_LEGACY: {
// Grant an install permission.
if (permissionsState.grantInstallPermission(bp) !=
PermissionsState.PERMISSION_OPERATION_FAILURE) {
@@ -11621,7 +11663,7 @@
true /* extract libs */);
} catch (PackageManagerException pme) {
Slog.e(TAG, "Error deriving application ABI", pme);
- res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error ");
+ res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
return;
}
diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java
index 8942325..ad662be 100644
--- a/services/core/java/com/android/server/pm/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/PermissionsState.java
@@ -381,10 +381,10 @@
*
* @return The gids for all device users.
*/
- public int[] computeGids() {
+ public int[] computeGids(int[] userIds) {
int[] gids = mGlobalGids;
- for (int userId : UserManagerService.getInstance().getUserIds()) {
+ for (int userId : userIds) {
final int[] userGids = computeGids(userId);
gids = appendInts(gids, userGids);
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 76ef19f..d2a135c 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2022,83 +2022,8 @@
|FileUtils.S_IRGRP|FileUtils.S_IWGRP,
-1, -1);
- // Write package list file now, use a JournaledFile.
- File tempFile = new File(mPackageListFilename.getAbsolutePath() + ".tmp");
- JournaledFile journal = new JournaledFile(mPackageListFilename, tempFile);
-
- final File writeTarget = journal.chooseForWrite();
- fstr = new FileOutputStream(writeTarget);
- str = new BufferedOutputStream(fstr);
- try {
- FileUtils.setPermissions(fstr.getFD(), 0640, SYSTEM_UID, PACKAGE_INFO_GID);
-
- StringBuilder sb = new StringBuilder();
- for (final PackageSetting pkg : mPackages.values()) {
- if (pkg.pkg == null || pkg.pkg.applicationInfo == null) {
- Slog.w(TAG, "Skipping " + pkg + " due to missing metadata");
- continue;
- }
-
- final ApplicationInfo ai = pkg.pkg.applicationInfo;
- final String dataPath = ai.dataDir;
- final boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
- final int[] gids = pkg.getPermissionsState().computeGids();
-
- // Avoid any application that has a space in its path.
- if (dataPath.indexOf(" ") >= 0)
- continue;
-
- // we store on each line the following information for now:
- //
- // pkgName - package name
- // userId - application-specific user id
- // debugFlag - 0 or 1 if the package is debuggable.
- // dataPath - path to package's data path
- // seinfo - seinfo label for the app (assigned at install time)
- // gids - supplementary gids this app launches with
- //
- // NOTE: We prefer not to expose all ApplicationInfo flags for now.
- //
- // DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS
- // FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES:
- // system/core/logd/LogStatistics.cpp
- // system/core/run-as/run-as.c
- // system/core/sdcard/sdcard.c
- // external/libselinux/src/android.c:package_info_init()
- //
- sb.setLength(0);
- sb.append(ai.packageName);
- sb.append(" ");
- sb.append((int)ai.uid);
- sb.append(isDebug ? " 1 " : " 0 ");
- sb.append(dataPath);
- sb.append(" ");
- sb.append(ai.seinfo);
- sb.append(" ");
- if (gids != null && gids.length > 0) {
- sb.append(gids[0]);
- for (int i = 1; i < gids.length; i++) {
- sb.append(",");
- sb.append(gids[i]);
- }
- } else {
- sb.append("none");
- }
- sb.append("\n");
- str.write(sb.toString().getBytes());
- }
- str.flush();
- FileUtils.sync(fstr);
- str.close();
- journal.commit();
- } catch (Exception e) {
- Slog.wtf(TAG, "Failed to write packages.list", e);
- IoUtils.closeQuietly(str);
- journal.rollback();
- }
-
+ writePackageListLPr();
writeAllUsersPackageRestrictionsLPr();
-
writeAllRuntimePermissionsLPr();
return;
@@ -2119,6 +2044,99 @@
//Debug.stopMethodTracing();
}
+ void writePackageListLPr() {
+ writePackageListLPr(-1);
+ }
+
+ void writePackageListLPr(int creatingUserId) {
+ // Only derive GIDs for active users (not dying)
+ final List<UserInfo> users = UserManagerService.getInstance().getUsers(true);
+ int[] userIds = new int[users.size()];
+ for (int i = 0; i < userIds.length; i++) {
+ userIds[i] = users.get(i).id;
+ }
+ if (creatingUserId != -1) {
+ userIds = ArrayUtils.appendInt(userIds, creatingUserId);
+ }
+
+ // Write package list file now, use a JournaledFile.
+ File tempFile = new File(mPackageListFilename.getAbsolutePath() + ".tmp");
+ JournaledFile journal = new JournaledFile(mPackageListFilename, tempFile);
+
+ final File writeTarget = journal.chooseForWrite();
+ FileOutputStream fstr = null;
+ BufferedOutputStream str = null;
+ try {
+ fstr = new FileOutputStream(writeTarget);
+ str = new BufferedOutputStream(fstr);
+ FileUtils.setPermissions(fstr.getFD(), 0640, SYSTEM_UID, PACKAGE_INFO_GID);
+
+ StringBuilder sb = new StringBuilder();
+ for (final PackageSetting pkg : mPackages.values()) {
+ if (pkg.pkg == null || pkg.pkg.applicationInfo == null) {
+ Slog.w(TAG, "Skipping " + pkg + " due to missing metadata");
+ continue;
+ }
+
+ final ApplicationInfo ai = pkg.pkg.applicationInfo;
+ final String dataPath = ai.dataDir;
+ final boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ final int[] gids = pkg.getPermissionsState().computeGids(userIds);
+
+ // Avoid any application that has a space in its path.
+ if (dataPath.indexOf(" ") >= 0)
+ continue;
+
+ // we store on each line the following information for now:
+ //
+ // pkgName - package name
+ // userId - application-specific user id
+ // debugFlag - 0 or 1 if the package is debuggable.
+ // dataPath - path to package's data path
+ // seinfo - seinfo label for the app (assigned at install time)
+ // gids - supplementary gids this app launches with
+ //
+ // NOTE: We prefer not to expose all ApplicationInfo flags for now.
+ //
+ // DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS
+ // FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES:
+ // system/core/logd/LogStatistics.cpp
+ // system/core/run-as/run-as.c
+ // system/core/sdcard/sdcard.c
+ // external/libselinux/src/android.c:package_info_init()
+ //
+ sb.setLength(0);
+ sb.append(ai.packageName);
+ sb.append(" ");
+ sb.append((int)ai.uid);
+ sb.append(isDebug ? " 1 " : " 0 ");
+ sb.append(dataPath);
+ sb.append(" ");
+ sb.append(ai.seinfo);
+ sb.append(" ");
+ if (gids != null && gids.length > 0) {
+ sb.append(gids[0]);
+ for (int i = 1; i < gids.length; i++) {
+ sb.append(",");
+ sb.append(gids[i]);
+ }
+ } else {
+ sb.append("none");
+ }
+ sb.append("\n");
+ str.write(sb.toString().getBytes());
+ }
+ str.flush();
+ FileUtils.sync(fstr);
+ str.close();
+ journal.commit();
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Failed to write packages.list", e);
+ IoUtils.closeQuietly(str);
+ journal.rollback();
+ }
+ }
+
void writeDisabledSysPackageLPr(XmlSerializer serializer, final PackageSetting pkg)
throws java.io.IOException {
serializer.startTag(null, "updated-package");
@@ -3491,6 +3509,7 @@
}
readDefaultPreferredAppsLPw(service, userHandle);
writePackageRestrictionsLPr(userHandle);
+ writePackageListLPr(userHandle);
}
void removeUserLPw(int userId) {
@@ -3506,6 +3525,8 @@
removeCrossProfileIntentFiltersLPw(userId);
mRuntimePermissionsPersistence.onUserRemoved(userId);
+
+ writePackageListLPr();
}
void removeCrossProfileIntentFiltersLPw(int userId) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a4e9c68..9bb5e40 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -51,7 +51,6 @@
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.media.session.MediaSessionLegacyHelper;
-import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.FactoryTest;
@@ -97,7 +96,6 @@
import android.view.Surface;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.ViewRootImpl;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -458,7 +456,7 @@
// menu needs to be displayed.
boolean mLastFocusNeedsMenu = false;
- FakeWindow mHideNavFakeWindow = null;
+ InputConsumer mInputConsumer = null;
static final Rect mTmpParentFrame = new Rect();
static final Rect mTmpDisplayFrame = new Rect();
@@ -1817,7 +1815,7 @@
case TYPE_APPLICATION_STARTING:
case TYPE_BOOT_PROGRESS:
case TYPE_DISPLAY_OVERLAY:
- case TYPE_HIDDEN_NAV_CONSUMER:
+ case TYPE_INPUT_CONSUMER:
case TYPE_KEYGUARD_SCRIM:
case TYPE_KEYGUARD_DIALOG:
case TYPE_MAGNIFICATION_OVERLAY:
@@ -1942,75 +1940,75 @@
case TYPE_VOICE_INTERACTION:
// voice interaction layer is almost immediately above apps.
return 5;
- case TYPE_SYSTEM_DIALOG:
+ case TYPE_INPUT_CONSUMER:
return 6;
+ case TYPE_SYSTEM_DIALOG:
+ return 7;
case TYPE_TOAST:
// toasts and the plugged-in battery thing
- return 7;
+ return 8;
case TYPE_PRIORITY_PHONE:
// SIM errors and unlock. Not sure if this really should be in a high layer.
- return 8;
+ return 9;
case TYPE_DREAM:
// used for Dreams (screensavers with TYPE_DREAM windows)
- return 9;
+ return 10;
case TYPE_SYSTEM_ALERT:
// like the ANR / app crashed dialogs
- return 10;
+ return 11;
case TYPE_INPUT_METHOD:
// on-screen keyboards and other such input method user interfaces go here.
- return 11;
+ return 12;
case TYPE_INPUT_METHOD_DIALOG:
// on-screen keyboards and other such input method user interfaces go here.
- return 12;
+ return 13;
case TYPE_KEYGUARD_SCRIM:
// the safety window that shows behind keyguard while keyguard is starting
- return 13;
- case TYPE_STATUS_BAR_SUB_PANEL:
return 14;
- case TYPE_STATUS_BAR:
+ case TYPE_STATUS_BAR_SUB_PANEL:
return 15;
- case TYPE_STATUS_BAR_PANEL:
+ case TYPE_STATUS_BAR:
return 16;
- case TYPE_KEYGUARD_DIALOG:
+ case TYPE_STATUS_BAR_PANEL:
return 17;
+ case TYPE_KEYGUARD_DIALOG:
+ return 18;
case TYPE_VOLUME_OVERLAY:
// the on-screen volume indicator and controller shown when the user
// changes the device volume
- return 18;
+ return 19;
case TYPE_SYSTEM_OVERLAY:
// the on-screen volume indicator and controller shown when the user
// changes the device volume
- return 19;
+ return 20;
case TYPE_NAVIGATION_BAR:
// the navigation bar, if available, shows atop most things
- return 20;
+ return 21;
case TYPE_NAVIGATION_BAR_PANEL:
// some panels (e.g. search) need to show on top of the navigation bar
- return 21;
+ return 22;
case TYPE_SYSTEM_ERROR:
// system-level error dialogs
- return 22;
+ return 23;
case TYPE_MAGNIFICATION_OVERLAY:
// used to highlight the magnified portion of a display
- return 23;
+ return 24;
case TYPE_DISPLAY_OVERLAY:
// used to simulate secondary display devices
- return 24;
+ return 25;
case TYPE_DRAG:
// the drag layer: input for drag-and-drop is associated with this window,
// which sits above all other focusable windows
- return 25;
+ return 26;
case TYPE_ACCESSIBILITY_OVERLAY:
// overlay put by accessibility services to intercept user interaction
- return 26;
- case TYPE_SECURE_SYSTEM_OVERLAY:
return 27;
- case TYPE_BOOT_PROGRESS:
+ case TYPE_SECURE_SYSTEM_OVERLAY:
return 28;
+ case TYPE_BOOT_PROGRESS:
+ return 29;
case TYPE_POINTER:
// the (mouse) pointer layer
- return 29;
- case TYPE_HIDDEN_NAV_CONSUMER:
return 30;
}
Log.e(TAG, "Unknown window type: " + type);
@@ -3385,15 +3383,13 @@
// detect when the user presses anywhere to bring back the nav
// bar and ensure the application doesn't see the event.
if (navVisible || navAllowedHidden) {
- if (mHideNavFakeWindow != null) {
- mHideNavFakeWindow.dismiss();
- mHideNavFakeWindow = null;
+ if (mInputConsumer != null) {
+ mInputConsumer.dismiss();
+ mInputConsumer = null;
}
- } else if (mHideNavFakeWindow == null) {
- mHideNavFakeWindow = mWindowManagerFuncs.addFakeWindow(
- mHandler.getLooper(), mHideNavInputEventReceiverFactory,
- "hidden nav", WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER, 0,
- 0, false, false, true);
+ } else if (mInputConsumer == null) {
+ mInputConsumer = mWindowManagerFuncs.addInputConsumer(mHandler.getLooper(),
+ mHideNavInputEventReceiverFactory);
}
// For purposes of positioning and showing the nav bar, if we have
@@ -6311,7 +6307,8 @@
vis = mNavigationBarController.applyTranslucentFlagLw(transWin, vis, oldVis);
// prevent status bar interaction from clearing certain flags
- boolean statusBarHasFocus = win.getAttrs().type == TYPE_STATUS_BAR;
+ int type = win.getAttrs().type;
+ boolean statusBarHasFocus = type == TYPE_STATUS_BAR;
if (statusBarHasFocus && !isStatusBarKeyguard()) {
int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
@@ -6323,6 +6320,11 @@
}
vis = (vis & ~flags) | (oldVis & flags);
}
+ if (windowTypeToLayerLw(type) > windowTypeToLayerLw(TYPE_INPUT_CONSUMER)) {
+ // We can't get into fullscreen from this window otherwise the consumer would not get
+ // the input events.
+ vis = (vis & ~View.SYSTEM_UI_FLAG_FULLSCREEN);
+ }
if (!areTranslucentBarsAllowed() && transWin != mStatusBar) {
vis &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSLUCENT
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 482ae24..66ae9ef 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1248,7 +1248,7 @@
&& windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
&& windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
&& windowType != WindowManager.LayoutParams.TYPE_DRAG
- && windowType != WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER
+ && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER
&& windowType != WindowManager.LayoutParams.TYPE_POINTER
&& windowType != WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY
&& windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
diff --git a/services/core/java/com/android/server/wm/FakeWindowImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
similarity index 77%
rename from services/core/java/com/android/server/wm/FakeWindowImpl.java
rename to services/core/java/com/android/server/wm/InputConsumerImpl.java
index 1136ced..0581a16 100644
--- a/services/core/java/com/android/server/wm/FakeWindowImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -16,17 +16,18 @@
package com.android.server.wm;
-import com.android.server.input.InputApplicationHandle;
-import com.android.server.input.InputWindowHandle;
-
import android.os.Looper;
import android.os.Process;
import android.view.Display;
import android.view.InputChannel;
import android.view.InputEventReceiver;
+import android.view.WindowManager;
import android.view.WindowManagerPolicy;
-public final class FakeWindowImpl implements WindowManagerPolicy.FakeWindow {
+import com.android.server.input.InputApplicationHandle;
+import com.android.server.input.InputWindowHandle;
+
+public final class InputConsumerImpl implements WindowManagerPolicy.InputConsumer {
final WindowManagerService mService;
final InputChannel mServerChannel, mClientChannel;
final InputApplicationHandle mApplicationHandle;
@@ -34,12 +35,9 @@
final InputEventReceiver mInputEventReceiver;
final int mWindowLayer;
- boolean mTouchFullscreen;
-
- public FakeWindowImpl(WindowManagerService service,
- Looper looper, InputEventReceiver.Factory inputEventReceiverFactory,
- String name, int windowType, int layoutParamsFlags,
- boolean canReceiveKeys, boolean hasFocus, boolean touchFullscreen) {
+ public InputConsumerImpl(WindowManagerService service, Looper looper,
+ InputEventReceiver.Factory inputEventReceiverFactory) {
+ String name = "input consumer";
mService = service;
InputChannel[] channels = InputChannel.openInputChannelPair(name);
@@ -58,31 +56,25 @@
mWindowHandle = new InputWindowHandle(mApplicationHandle, null, Display.DEFAULT_DISPLAY);
mWindowHandle.name = name;
mWindowHandle.inputChannel = mServerChannel;
- mWindowLayer = getLayerLw(windowType);
+ mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
+ mWindowLayer = getLayerLw(mWindowHandle.layoutParamsType);
mWindowHandle.layer = mWindowLayer;
- mWindowHandle.layoutParamsFlags = layoutParamsFlags;
- mWindowHandle.layoutParamsType = windowType;
+ mWindowHandle.layoutParamsFlags = 0;
mWindowHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
mWindowHandle.visible = true;
- mWindowHandle.canReceiveKeys = canReceiveKeys;
- mWindowHandle.hasFocus = hasFocus;
+ mWindowHandle.canReceiveKeys = false;
+ mWindowHandle.hasFocus = false;
mWindowHandle.hasWallpaper = false;
mWindowHandle.paused = false;
mWindowHandle.ownerPid = Process.myPid();
mWindowHandle.ownerUid = Process.myUid();
mWindowHandle.inputFeatures = 0;
mWindowHandle.scaleFactor = 1.0f;
-
- mTouchFullscreen = touchFullscreen;
}
void layout(int dw, int dh) {
- if (mTouchFullscreen) {
- mWindowHandle.touchableRegion.set(0, 0, dw, dh);
- } else {
- mWindowHandle.touchableRegion.setEmpty();
- }
+ mWindowHandle.touchableRegion.set(0, 0, dw, dh);
mWindowHandle.frameLeft = 0;
mWindowHandle.frameTop = 0;
mWindowHandle.frameRight = dw;
@@ -92,7 +84,7 @@
@Override
public void dismiss() {
synchronized (mService.mWindowMap) {
- if (mService.removeFakeWindowLocked(this)) {
+ if (mService.removeInputConsumer()) {
mInputEventReceiver.dispose();
mService.mInputManager.unregisterInputChannel(mServerChannel);
mClientChannel.dispose();
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index c24fcb3..ae442e5 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -16,10 +16,6 @@
package com.android.server.wm;
-import com.android.server.input.InputManagerService;
-import com.android.server.input.InputApplicationHandle;
-import com.android.server.input.InputWindowHandle;
-
import android.app.ActivityManagerNative;
import android.graphics.Rect;
import android.os.RemoteException;
@@ -30,17 +26,21 @@
import android.view.KeyEvent;
import android.view.WindowManager;
+import com.android.server.input.InputApplicationHandle;
+import com.android.server.input.InputManagerService;
+import com.android.server.input.InputWindowHandle;
+
import java.util.Arrays;
final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
private final WindowManagerService mService;
-
+
// Current window with input focus for keys and other non-touch events. May be null.
private WindowState mInputFocus;
-
+
// When true, prevents input dispatch from proceeding until set to false again.
private boolean mInputDispatchFrozen;
-
+
// When true, input dispatch proceeds normally. Otherwise all events are dropped.
// Initially false, so that input does not get dispatched until boot is finished at
// which point the ActivityManager will enable dispatching.
@@ -256,10 +256,7 @@
}
}
- final int NFW = mService.mFakeWindows.size();
- for (int i = 0; i < NFW; i++) {
- addInputWindowHandleLw(mService.mFakeWindows.get(i).mWindowHandle);
- }
+ boolean addInputConsumerHandle = mService.mInputConsumer != null;
// Add all windows on the default display.
final int numDisplays = mService.mDisplayContents.size();
@@ -273,6 +270,11 @@
// Skip this window because it cannot possibly receive input.
continue;
}
+ if (addInputConsumerHandle
+ && inputWindowHandle.layer <= mService.mInputConsumer.mWindowHandle.layer) {
+ addInputWindowHandleLw(mService.mInputConsumer.mWindowHandle);
+ addInputConsumerHandle = false;
+ }
final int flags = child.mAttrs.flags;
final int privateFlags = child.mAttrs.privateFlags;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b84b506..cebb909 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -108,7 +108,6 @@
import android.view.WindowManagerGlobal;
import android.view.WindowManagerInternal;
import android.view.WindowManagerPolicy;
-import android.view.WindowManagerPolicy.FakeWindow;
import android.view.WindowManagerPolicy.PointerEventListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -382,10 +381,10 @@
final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();
/**
- * Fake windows added to the window manager. Note: ordered from top to
- * bottom, opposite of mWindows.
+ * The input consumer added to the window manager which consumes input events to windows below
+ * it.
*/
- final ArrayList<FakeWindowImpl> mFakeWindows = new ArrayList<>();
+ InputConsumerImpl mInputConsumer;
/**
* Windows that are being resized. Used so we can tell the client about
@@ -3187,10 +3186,13 @@
}
}
if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) {
- // To change the format, we need to re-build the surface.
- winAnimator.destroySurfaceLocked();
- toBeDisplayed = true;
- surfaceChanged = true;
+ // If the format can be changed in place yaay!
+ // If not, fall back to a surface re-build
+ if (!winAnimator.tryChangeFormatInPlaceLocked()) {
+ winAnimator.destroySurfaceLocked();
+ toBeDisplayed = true;
+ surfaceChanged = true;
+ }
}
try {
if (!win.mHasSurface) {
@@ -8963,9 +8965,8 @@
final int dw = displayInfo.logicalWidth;
final int dh = displayInfo.logicalHeight;
- final int NFW = mFakeWindows.size();
- for (int i=0; i<NFW; i++) {
- mFakeWindows.get(i).layout(dw, dh);
+ if (mInputConsumer != null) {
+ mInputConsumer.layout(dw, dh);
}
final int N = windows.size();
@@ -10992,28 +10993,19 @@
}
@Override
- public FakeWindow addFakeWindow(Looper looper,
- InputEventReceiver.Factory inputEventReceiverFactory,
- String name, int windowType, int layoutParamsFlags, int layoutParamsPrivateFlags,
- boolean canReceiveKeys, boolean hasFocus, boolean touchFullscreen) {
+ public InputConsumerImpl addInputConsumer(Looper looper,
+ InputEventReceiver.Factory inputEventReceiverFactory) {
synchronized (mWindowMap) {
- FakeWindowImpl fw = new FakeWindowImpl(this, looper, inputEventReceiverFactory,
- name, windowType, layoutParamsFlags, canReceiveKeys, hasFocus, touchFullscreen);
- int i=0;
- while (i<mFakeWindows.size()) {
- if (mFakeWindows.get(i).mWindowLayer <= fw.mWindowLayer) {
- break;
- }
- }
- mFakeWindows.add(i, fw);
+ mInputConsumer = new InputConsumerImpl(this, looper, inputEventReceiverFactory);
mInputMonitor.updateInputWindowsLw(true);
- return fw;
+ return mInputConsumer;
}
}
- boolean removeFakeWindowLocked(FakeWindow window) {
+ boolean removeInputConsumer() {
synchronized (mWindowMap) {
- if (mFakeWindows.remove(window)) {
+ if (mInputConsumer != null) {
+ mInputConsumer = null;
mInputMonitor.updateInputWindowsLw(true);
return true;
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 424e2e2..e9023fd 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -146,6 +146,9 @@
boolean mKeyguardGoingAwayAnimation;
+ /** The pixel format of the underlying SurfaceControl */
+ int mSurfaceFormat;
+
/** This is set when there is no Surface */
static final int NO_SURFACE = 0;
/** This is set after the Surface has been created but before the window has been drawn. During
@@ -845,6 +848,7 @@
flags |= SurfaceControl.OPAQUE;
}
+ mSurfaceFormat = format;
if (DEBUG_SURFACE_TRACE) {
mSurfaceControl = new SurfaceTrace(
mSession.mSurfaceSession,
@@ -1610,6 +1614,28 @@
}
}
+ /**
+ * Try to change the pixel format without recreating the surface. This
+ * will be common in the case of changing from PixelFormat.OPAQUE to
+ * PixelFormat.TRANSLUCENT in the hardware-accelerated case as both
+ * requested formats resolve to the same underlying SurfaceControl format
+ * @return True if format was succesfully changed, false otherwise
+ */
+ boolean tryChangeFormatInPlaceLocked() {
+ if (mSurfaceControl == null) {
+ return false;
+ }
+ final LayoutParams attrs = mWin.getAttrs();
+ final boolean isHwAccelerated = (attrs.flags &
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
+ final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
+ if (format == mSurfaceFormat) {
+ setOpaqueLocked(!PixelFormat.formatHasAlpha(attrs.format));
+ return true;
+ }
+ return false;
+ }
+
void setOpaqueLocked(boolean isOpaque) {
if (mSurfaceControl == null) {
return;
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index a5546cf..9556b08 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -10,7 +10,6 @@
$(LOCAL_REL_DIR)/com_android_server_AssetAtlasService.cpp \
$(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \
$(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \
- $(LOCAL_REL_DIR)/com_android_server_fingerprint_FingerprintService.cpp \
$(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecController.cpp \
$(LOCAL_REL_DIR)/com_android_server_input_InputApplicationHandle.cpp \
$(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 7db7414..67872da 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -41,7 +41,6 @@
int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
int register_android_server_tv_TvInputHal(JNIEnv* env);
int register_android_server_PersistentDataBlockService(JNIEnv* env);
-int register_android_server_fingerprint_FingerprintService(JNIEnv* env);
int register_android_server_Watchdog(JNIEnv* env);
};
@@ -79,7 +78,6 @@
register_android_server_hdmi_HdmiCecController(env);
register_android_server_tv_TvInputHal(env);
register_android_server_PersistentDataBlockService(env);
- register_android_server_fingerprint_FingerprintService(env);
register_android_server_Watchdog(env);
return JNI_VERSION_1_4;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9ad7e11..a9e76d8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -6392,25 +6392,34 @@
}
@Override
- public boolean setPermissionGranted(ComponentName admin, String packageName,
- String permission, boolean granted) throws RemoteException {
+ public boolean setPermissionGrantState(ComponentName admin, String packageName,
+ String permission, int grantState) throws RemoteException {
UserHandle user = Binder.getCallingUserHandle();
synchronized (this) {
getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
long ident = Binder.clearCallingIdentity();
try {
PackageManager packageManager = mContext.getPackageManager();
- if (granted) {
- packageManager.grantRuntimePermission(packageName, permission, user);
- packageManager.updatePermissionFlags(permission, packageName,
- PackageManager.FLAG_PERMISSION_POLICY_FIXED,
- PackageManager.FLAG_PERMISSION_POLICY_FIXED, user);
- } else {
- packageManager.revokeRuntimePermission(packageName,
- permission, user);
- packageManager.updatePermissionFlags(permission, packageName,
- PackageManager.FLAG_PERMISSION_POLICY_FIXED,
- PackageManager.FLAG_PERMISSION_POLICY_FIXED, user);
+ switch (grantState) {
+ case DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED: {
+ packageManager.grantRuntimePermission(packageName, permission, user);
+ packageManager.updatePermissionFlags(permission, packageName,
+ PackageManager.FLAG_PERMISSION_POLICY_FIXED,
+ PackageManager.FLAG_PERMISSION_POLICY_FIXED, user);
+ } break;
+
+ case DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED: {
+ packageManager.revokeRuntimePermission(packageName,
+ permission, user);
+ packageManager.updatePermissionFlags(permission, packageName,
+ PackageManager.FLAG_PERMISSION_POLICY_FIXED,
+ PackageManager.FLAG_PERMISSION_POLICY_FIXED, user);
+ } break;
+
+ case DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT: {
+ packageManager.updatePermissionFlags(permission, packageName,
+ PackageManager.FLAG_PERMISSION_POLICY_FIXED, 0, user);
+ } break;
}
return true;
} catch (SecurityException se) {
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index d6ff475..a615675 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -131,6 +131,11 @@
usageStats.mBeginIdleTime = timeStamp;
}
+ void updateLastUsedTime(String packageName, long lastUsedTime) {
+ UsageStats usageStats = getOrCreateUsageStats(packageName);
+ usageStats.mLastTimeUsed = lastUsedTime;
+ }
+
void updateConfigurationStats(Configuration config, long timeStamp) {
if (activeConfiguration != null) {
ConfigurationStats activeStats = configurations.get(activeConfiguration);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index f7bcf2a..ff3bb28 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -99,7 +99,9 @@
private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
static final long DEFAULT_APP_IDLE_THRESHOLD_MILLIS = DEBUG ? ONE_MINUTE * 4
- : 1L * 24 * 60 * ONE_MINUTE; // 1 day
+ : 12 * 60 * ONE_MINUTE; // 12 hours of screen-on time sans dream-time
+ static final long DEFAULT_WALLCLOCK_APP_IDLE_THRESHOLD_MILLIS = DEBUG ? ONE_MINUTE * 8
+ : 2L * 24 * 60 * ONE_MINUTE; // 2 days
static final long DEFAULT_CHECK_IDLE_INTERVAL = DEBUG ? ONE_MINUTE
: 8 * 60 * ONE_MINUTE; // 8 hours
static final long DEFAULT_PAROLE_INTERVAL = DEBUG ? ONE_MINUTE * 10
@@ -356,7 +358,7 @@
final int packageCount = packages.size();
for (int p = 0; p < packageCount; p++) {
final String packageName = packages.get(p).packageName;
- final boolean isIdle = isAppIdleFiltered(packageName, userId);
+ final boolean isIdle = isAppIdleFiltered(packageName, userId, timeNow);
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
userId, isIdle ? 1 : 0, packageName));
mAppIdleHistory.addEntry(packageName, userId, isIdle, timeNow);
@@ -386,7 +388,8 @@
void updateDisplayLocked() {
boolean screenOn = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getState()
- != Display.STATE_OFF;
+ == Display.STATE_ON;
+
if (screenOn == mScreenOn) return;
mScreenOn = screenOn;
@@ -533,14 +536,16 @@
void reportEvent(UsageEvents.Event event, int userId) {
synchronized (mLock) {
final long timeNow = checkAndGetTimeLocked();
+ final long screenOnTime = getScreenOnTimeLocked(timeNow);
convertToSystemTimeLocked(event);
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
- final long lastUsed = service.getBeginIdleTime(event.mPackage);
- final long screenOnTime = getScreenOnTimeLocked(timeNow);
- final boolean previouslyIdle = hasPassedIdleTimeout(lastUsed, screenOnTime);
- service.reportEvent(event, screenOnTime);
+ final long beginIdleTime = service.getBeginIdleTime(event.mPackage);
+ final long lastUsedTime = service.getLastUsedTime(event.mPackage);
+ final boolean previouslyIdle = hasPassedIdleTimeoutLocked(beginIdleTime,
+ lastUsedTime, screenOnTime, timeNow);
+ service.reportEvent(event, getScreenOnTimeLocked(timeNow));
// Inform listeners if necessary
if ((event.mEventType == Event.MOVE_TO_FOREGROUND
|| event.mEventType == Event.MOVE_TO_BACKGROUND
@@ -556,8 +561,9 @@
}
/**
- * Forces the app's beginIdleTime to reflect idle or active. If idle, then it rolls back the
- * beginIdleTime to a point in time thats behind the threshold for idle.
+ * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
+ * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
+ * the threshold for idle.
*/
void forceIdleState(String packageName, int userId, boolean idle) {
synchronized (mLock) {
@@ -567,10 +573,13 @@
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
- final long lastUsed = service.getBeginIdleTime(packageName);
- final boolean previouslyIdle = hasPassedIdleTimeout(lastUsed,
- getScreenOnTimeLocked(timeNow));
+ final long beginIdleTime = service.getBeginIdleTime(packageName);
+ final long lastUsedTime = service.getLastUsedTime(packageName);
+ final boolean previouslyIdle = hasPassedIdleTimeoutLocked(beginIdleTime,
+ lastUsedTime, screenOnTime, timeNow);
service.setBeginIdleTime(packageName, deviceUsageTime);
+ service.setLastUsedTime(packageName,
+ timeNow - (idle ? DEFAULT_WALLCLOCK_APP_IDLE_THRESHOLD_MILLIS : 0) - 5000);
// Inform listeners if necessary
if (previouslyIdle != idle) {
// Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage);
@@ -650,13 +659,14 @@
}
}
- private boolean isAppIdleUnfiltered(String packageName, int userId) {
+ private boolean isAppIdleUnfiltered(String packageName, int userId, long timeNow) {
synchronized (mLock) {
- final long timeNow = checkAndGetTimeLocked();
+ final long screenOnTime = getScreenOnTimeLocked(timeNow);
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
long beginIdleTime = service.getBeginIdleTime(packageName);
- return hasPassedIdleTimeout(beginIdleTime, getScreenOnTimeLocked(timeNow));
+ long lastUsedTime = service.getLastUsedTime(packageName);
+ return hasPassedIdleTimeoutLocked(beginIdleTime, lastUsedTime, screenOnTime, timeNow);
}
}
@@ -665,8 +675,10 @@
* @param currentTime current time in device usage timebase
* @return whether it's been used far enough in the past to be considered inactive
*/
- boolean hasPassedIdleTimeout(long timestamp, long currentTime) {
- return timestamp <= currentTime - mAppIdleDurationMillis;
+ boolean hasPassedIdleTimeoutLocked(long beginIdleTime, long lastUsedTime,
+ long screenOnTime, long currentTime) {
+ return (beginIdleTime <= screenOnTime - mAppIdleDurationMillis)
+ && (lastUsedTime <= currentTime - DEFAULT_WALLCLOCK_APP_IDLE_THRESHOLD_MILLIS);
}
void addListener(AppIdleStateChangeListener listener) {
@@ -689,9 +701,12 @@
* This happens if the device is plugged in or temporarily allowed to make exceptions.
* Called by interface impls.
*/
- boolean isAppIdleFiltered(String packageName, int userId) {
+ boolean isAppIdleFiltered(String packageName, int userId, long timeNow) {
if (packageName == null) return false;
synchronized (mLock) {
+ if (timeNow == -1) {
+ timeNow = checkAndGetTimeLocked();
+ }
// Temporary exemption, probably due to device charging or occasional allowance to
// be allowed to sync, etc.
if (mAppIdleParoled) {
@@ -715,7 +730,7 @@
return false;
}
- return isAppIdleUnfiltered(packageName, userId);
+ return isAppIdleUnfiltered(packageName, userId, timeNow);
}
void setAppIdle(String packageName, boolean idle, int userId) {
@@ -948,7 +963,7 @@
}
final long token = Binder.clearCallingIdentity();
try {
- return UsageStatsService.this.isAppIdleFiltered(packageName, userId);
+ return UsageStatsService.this.isAppIdleFiltered(packageName, userId, -1);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1053,7 +1068,7 @@
@Override
public boolean isAppIdle(String packageName, int userId) {
- return UsageStatsService.this.isAppIdleFiltered(packageName, userId);
+ return UsageStatsService.this.isAppIdleFiltered(packageName, userId, -1);
}
@Override
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index b7e1c22..7c00dae 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -216,12 +216,19 @@
}
/**
- * Sets the last timestamp for each of the intervals.
- * @param lastTimestamp
+ * Sets the beginIdleTime for each of the intervals.
+ * @param beginIdleTime
*/
- void setBeginIdleTime(String packageName, long deviceUsageTime) {
+ void setBeginIdleTime(String packageName, long beginIdleTime) {
for (IntervalStats stats : mCurrentStats) {
- stats.updateBeginIdleTime(packageName, deviceUsageTime);
+ stats.updateBeginIdleTime(packageName, beginIdleTime);
+ }
+ notifyStatsChanged();
+ }
+
+ void setLastUsedTime(String packageName, long lastUsedTime) {
+ for (IntervalStats stats : mCurrentStats) {
+ stats.updateLastUsedTime(packageName, lastUsedTime);
}
notifyStatsChanged();
}
@@ -390,6 +397,16 @@
}
}
+ long getLastUsedTime(String packageName) {
+ final IntervalStats yearly = mCurrentStats[UsageStatsManager.INTERVAL_YEARLY];
+ UsageStats packageUsage;
+ if ((packageUsage = yearly.packageStats.get(packageName)) == null) {
+ return -1;
+ } else {
+ return packageUsage.getLastTimeUsed();
+ }
+ }
+
void persistActiveStats() {
if (mStatsChanged) {
Slog.i(TAG, mLogPrefix + "Flushing usage stats to disk");
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index fcdb6d6..35bdceb 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -151,7 +151,7 @@
// the user to have the default voice interaction service enabled.
// Note that we don't do this for low-RAM devices, since we aren't
// supporting voice interaction services there.
- curInteractorInfo = findAvailInteractor(userHandle, curRecognizer);
+ curInteractorInfo = findAvailInteractor(userHandle, curRecognizer.getPackageName());
if (curInteractorInfo != null) {
// Looks good! We'll apply this one. To make it happen, we clear the
// recognizer so that we don't think we have anything set and will
@@ -162,6 +162,18 @@
}
}
+ // If forceInteractorPackage exists, try to apply the interactor from this package if
+ // possible and ignore the regular interactor setting.
+ String forceInteractorPackage =
+ getForceVoiceInteractionServicePackage(mContext.getResources());
+ if (forceInteractorPackage != null) {
+ curInteractorInfo = findAvailInteractor(userHandle, forceInteractorPackage);
+ if (curInteractorInfo != null) {
+ // We'll apply this one. Clear the recognizer and re-apply the settings.
+ curRecognizer = null;
+ }
+ }
+
// If we are on a svelte device, make sure an interactor is not currently
// enabled; if it is, turn it off.
if (!mEnableService && curInteractorStr != null) {
@@ -225,8 +237,14 @@
private boolean shouldEnableService(Resources res) {
// VoiceInteractionService should not be enabled on low ram devices unless it has the config flag.
- return !ActivityManager.isLowRamDeviceStatic()
- || res.getBoolean(com.android.internal.R.bool.config_forceEnableVoiceInteractionService);
+ return !ActivityManager.isLowRamDeviceStatic() ||
+ getForceVoiceInteractionServicePackage(res) != null;
+ }
+
+ private String getForceVoiceInteractionServicePackage(Resources res) {
+ String interactorPackage =
+ res.getString(com.android.internal.R.string.config_forceVoiceInteractionServicePackage);
+ return TextUtils.isEmpty(interactorPackage) ? null : interactorPackage;
}
public void systemRunning(boolean safeMode) {
@@ -279,7 +297,7 @@
}
}
- VoiceInteractionServiceInfo findAvailInteractor(int userHandle, ComponentName recognizer) {
+ VoiceInteractionServiceInfo findAvailInteractor(int userHandle, String packageName) {
List<ResolveInfo> available =
mContext.getPackageManager().queryIntentServicesAsUser(
new Intent(VoiceInteractionService.SERVICE_INTERFACE), 0, userHandle);
@@ -300,8 +318,8 @@
VoiceInteractionServiceInfo info = new VoiceInteractionServiceInfo(
mContext.getPackageManager(), comp, userHandle);
if (info.getParseError() == null) {
- if (recognizer == null || info.getServiceInfo().packageName.equals(
- recognizer.getPackageName())) {
+ if (packageName == null || info.getServiceInfo().packageName.equals(
+ packageName)) {
if (foundInfo == null) {
foundInfo = info;
} else {
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index f05a1ef..a25d327 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -151,6 +151,7 @@
private final CharSequence mShortDescription;
private final List<String> mSupportedUriSchemes;
private final Icon mIcon;
+ private boolean mIsEnabled;
/**
* Helper class for creating a {@link PhoneAccount}.
@@ -165,6 +166,7 @@
private CharSequence mShortDescription;
private List<String> mSupportedUriSchemes = new ArrayList<String>();
private Icon mIcon;
+ private boolean mIsEnabled = false;
/**
* Creates a builder with the specified {@link PhoneAccountHandle} and label.
@@ -190,6 +192,7 @@
mShortDescription = phoneAccount.getShortDescription();
mSupportedUriSchemes.addAll(phoneAccount.getSupportedUriSchemes());
mIcon = phoneAccount.getIcon();
+ mIsEnabled = phoneAccount.isEnabled();
}
/**
@@ -288,6 +291,18 @@
}
/**
+ * Sets the enabled state of the phone account.
+ *
+ * @param isEnabled The enabled state.
+ * @return The builder.
+ * @hide
+ */
+ public Builder setIsEnabled(boolean isEnabled) {
+ mIsEnabled = isEnabled;
+ return this;
+ }
+
+ /**
* Creates an instance of a {@link PhoneAccount} based on the current builder settings.
*
* @return The {@link PhoneAccount}.
@@ -307,7 +322,8 @@
mHighlightColor,
mLabel,
mShortDescription,
- mSupportedUriSchemes);
+ mSupportedUriSchemes,
+ mIsEnabled);
}
}
@@ -320,7 +336,8 @@
int highlightColor,
CharSequence label,
CharSequence shortDescription,
- List<String> supportedUriSchemes) {
+ List<String> supportedUriSchemes,
+ boolean isEnabled) {
mAccountHandle = account;
mAddress = address;
mSubscriptionAddress = subscriptionAddress;
@@ -330,6 +347,7 @@
mLabel = label;
mShortDescription = shortDescription;
mSupportedUriSchemes = Collections.unmodifiableList(supportedUriSchemes);
+ mIsEnabled = isEnabled;
}
public static Builder builder(
@@ -437,6 +455,15 @@
}
/**
+ * Indicates whether the user has enabled this phone account or not {@code PhoneAccounts}.
+ *
+ * @return The {@code true} if the account is enabled by the user, {@code false} otherwise.
+ */
+ public boolean isEnabled() {
+ return mIsEnabled;
+ }
+
+ /**
* Determines if the {@link PhoneAccount} supports calls to/from addresses with a specified URI
* scheme.
*
@@ -466,6 +493,14 @@
return mHighlightColor;
}
+ /**
+ * Sets the enabled state of the phone account.
+ * @hide
+ */
+ public void setIsEnabled(boolean isEnabled) {
+ mIsEnabled = isEnabled;
+ }
+
//
// Parcelable implementation
//
@@ -500,12 +535,14 @@
out.writeCharSequence(mLabel);
out.writeCharSequence(mShortDescription);
out.writeStringList(mSupportedUriSchemes);
+
if (mIcon == null) {
out.writeInt(0);
} else {
out.writeInt(1);
mIcon.writeToParcel(out, flags);
}
+ out.writeByte((byte) (mIsEnabled ? 1 : 0));
}
public static final Creator<PhoneAccount> CREATOR
@@ -547,11 +584,14 @@
} else {
mIcon = null;
}
+ mIsEnabled = in.readByte() == 1;
}
@Override
public String toString() {
- StringBuilder sb = new StringBuilder().append("[PhoneAccount: ")
+ StringBuilder sb = new StringBuilder().append("[[")
+ .append(mIsEnabled ? 'X' : ' ')
+ .append("] PhoneAccount: ")
.append(mAccountHandle)
.append(" Capabilities: ")
.append(mCapabilities)
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index c8ed2b0..308c204 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -377,15 +377,23 @@
}
/**
- * Return the {@link PhoneAccount} which is the user-chosen default for making outgoing phone
- * calls with a specified URI scheme.
+ * Return the {@link PhoneAccount} which will be used to place outgoing calls to addresses with
+ * the specified {@code uriScheme}. This {@link PhoneAccount} will always be a member of the
+ * list which is returned from invoking {@link #getCallCapablePhoneAccounts()}. The specific
+ * account returned depends on the following priorities:
+ * <ul>
+ * <li> If the user-selected default {@link PhoneAccount} supports the specified scheme, it will
+ * be returned.
+ * </li>
+ * <li> If there exists only one {@link PhoneAccount} that supports the specified scheme, it
+ * will be returned.
+ * </li>
+ * </ul>
* <p>
- * Apps must be prepared for this method to return {@code null}, indicating that there currently
- * exists no user-chosen default {@code PhoneAccount}.
- * <p>
+ * If no {@link PhoneAccount} fits the criteria above, this method will return {@code null}.
+ *
* @param uriScheme The URI scheme.
- * @return The {@link PhoneAccountHandle} corresponding to the user-chosen default for outgoing
- * phone calls for a specified URI scheme.
+ * @return The {@link PhoneAccountHandle} corresponding to the account to be used.
*/
public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) {
try {
@@ -403,7 +411,7 @@
* Return the {@link PhoneAccount} which is the user-chosen default for making outgoing phone
* calls. This {@code PhoneAccount} will always be a member of the list which is returned from
* calling {@link #getCallCapablePhoneAccounts()}
- *
+ * <p>
* Apps must be prepared for this method to return {@code null}, indicating that there currently
* exists no user-chosen default {@code PhoneAccount}.
*
@@ -422,7 +430,7 @@
}
/**
- * Sets the default account for making outgoing phone calls.
+ * Sets the user-chosen default for making outgoing phone calls.
* @hide
*/
public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
@@ -439,6 +447,7 @@
* Returns the current SIM call manager. Apps must be prepared for this method to return
* {@code null}, indicating that there currently exists no user-chosen default
* {@code PhoneAccount}.
+ *
* @return The phone account handle of the current sim call manager.
*/
public PhoneAccountHandle getSimCallManager() {
@@ -454,6 +463,7 @@
/**
* Sets the SIM call manager to the specified phone account.
+ *
* @param accountHandle The phone account handle of the account to set as the sim call manager.
* @hide
*/
@@ -469,6 +479,7 @@
/**
* Returns the list of registered SIM call managers.
+ *
* @return List of registered SIM call managers.
* @hide
*/
@@ -497,16 +508,6 @@
}
/**
- * Returns the list of registered SIM call managers.
- * @return List of registered SIM call managers.
- * @hide
- */
- @SystemApi
- public List<PhoneAccountHandle> getRegisteredConnectionManagers() {
- return getSimCallManagers();
- }
-
- /**
* Returns a list of the {@link PhoneAccountHandle}s which can be used to make and receive phone
* calls which support the specified URI scheme.
* <P>
@@ -534,20 +535,33 @@
/**
- * Return a list of {@link PhoneAccountHandle}s which can be used to make and receive phone
- * calls.
+ * Returns a list of {@link PhoneAccountHandle}s which can be used to make and receive phone
+ * calls. The returned list includes only those accounts which have been explicitly enabled
+ * by the user.
*
* @see #EXTRA_PHONE_ACCOUNT_HANDLE
* @return A list of {@code PhoneAccountHandle} objects.
- *
*/
public List<PhoneAccountHandle> getCallCapablePhoneAccounts() {
+ return getCallCapablePhoneAccounts(false);
+ }
+
+ /**
+ * Returns a list of {@link PhoneAccountHandle}s including those which have not been enabled
+ * by the user.
+ *
+ * @return A list of {@code PhoneAccountHandle} objects.
+ * @hide
+ */
+ public List<PhoneAccountHandle> getCallCapablePhoneAccounts(boolean includeDisabledAccounts) {
try {
if (isServiceConnected()) {
- return getTelecomService().getCallCapablePhoneAccounts(mContext.getOpPackageName());
+ return getTelecomService().getCallCapablePhoneAccounts(
+ includeDisabledAccounts, mContext.getOpPackageName());
}
} catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts", e);
+ Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts(" +
+ includeDisabledAccounts + ")", e);
}
return new ArrayList<>();
}
@@ -1163,6 +1177,25 @@
}
}
+ /**
+ * Enables and disables specified phone account.
+ *
+ * @param handle Handle to the phone account.
+ * @param isEnabled Enable state of the phone account.
+ * @hide
+ */
+ @SystemApi
+ public void enablePhoneAccount(PhoneAccountHandle handle, boolean isEnabled) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.enablePhoneAccount(handle, isEnabled);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error enablePhoneAbbount", e);
+ }
+ }
+ }
+
private ITelecomService getTelecomService() {
return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index bc76f06..aa02021 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -53,7 +53,8 @@
/**
* @see TelecomServiceImpl#getCallCapablePhoneAccounts
*/
- List<PhoneAccountHandle> getCallCapablePhoneAccounts(String callingPackage);
+ List<PhoneAccountHandle> getCallCapablePhoneAccounts(
+ boolean includeDisabledAccounts, String callingPackage);
/**
* @see TelecomManager#getPhoneAccountsSupportingScheme
@@ -226,4 +227,9 @@
* @see TelecomServiceImpl#placeCall
*/
void placeCall(in Uri handle, in Bundle extras, String callingPackage);
+
+ /**
+ * @see TelecomServiceImpl#enablePhoneAccount
+ */
+ void enablePhoneAccount(in PhoneAccountHandle accountHandle, boolean isEnabled);
}
diff --git a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/fbosync.rs b/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/fbosync.rs
index 42b1cf1..0c177ef 100644
--- a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/fbosync.rs
+++ b/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/fbosync.rs
@@ -116,7 +116,6 @@
rs_allocation allMeshes = rsGetAllocation(gMeshes);
int size = rsAllocationGetDimX(allMeshes);
gLookAt = 0.0f;
- float minX, minY, minZ, maxX, maxY, maxZ;
for (int i = 0; i < size; i++) {
MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i);
rsgDrawMesh(info->mMesh);
@@ -124,7 +123,6 @@
}
static void drawDescription() {
- uint width = rsgGetWidth();
uint height = rsgGetHeight();
int left = 0, right = 0, top = 0, bottom = 0;
@@ -196,7 +194,6 @@
uint32_t w = rsAllocationGetDimX(gOffscreen);
uint32_t h = rsAllocationGetDimY(gOffscreen);
- uint32_t numElements = w*h;
rsgAllocationSyncAll(gOffscreen, RS_ALLOCATION_USAGE_GRAPHICS_RENDER_TARGET);
diff --git a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/fbotest.rs b/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/fbotest.rs
index 05ef3ac..13a3c85 100644
--- a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/fbotest.rs
+++ b/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/fbotest.rs
@@ -115,7 +115,6 @@
rs_allocation allMeshes = rsGetAllocation(gMeshes);
int size = rsAllocationGetDimX(allMeshes);
gLookAt = 0.0f;
- float minX, minY, minZ, maxX, maxY, maxZ;
for (int i = 0; i < size; i++) {
MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i);
rsgDrawMesh(info->mMesh);
@@ -123,7 +122,6 @@
}
static void drawDescription() {
- uint width = rsgGetWidth();
uint height = rsgGetHeight();
int left = 0, right = 0, top = 0, bottom = 0;
diff --git a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/simplemodel.rs b/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/simplemodel.rs
index de2a0a7..d3dd5b9 100644
--- a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/simplemodel.rs
+++ b/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/simplemodel.rs
@@ -123,7 +123,6 @@
rs_allocation allMeshes = rsGetAllocation(gMeshes);
int size = rsAllocationGetDimX(allMeshes);
gLookAt = 0.0f;
- float minX, minY, minZ, maxX, maxY, maxZ;
for (int i = 0; i < size; i++) {
MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i);
rsgDrawMesh(info->mMesh);
@@ -131,7 +130,6 @@
}
void drawDescription() {
- uint width = rsgGetWidth();
uint height = rsgGetHeight();
int left = 0, right = 0, top = 0, bottom = 0;
@@ -163,7 +161,7 @@
rsMatrixMultiply(&matrix, &gPostureMatrix);
rsMatrixRotate(&matrix, gRotateX, 1.0f, 0.0f, 0.0f);
rsMatrixRotate(&matrix, gRotateY, 0.0f, 1.0f, 0.0f);
-
+
rsgProgramVertexLoadModelMatrix(&matrix);
renderAllMeshes();
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
index 27e5b11..43cf4e0 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
@@ -85,7 +85,7 @@
TestData testData;
fillSurfaceParams(&testData);
- rs_allocation null_alloc;
+ rs_allocation null_alloc = {0};
rsForEach(gTestScripts[index].testScript,
gTestScripts[index].testData,
null_alloc,
@@ -125,7 +125,6 @@
static int benchMode = 0;
static bool benchmarkSingleTest = false;
-static int benchSubMode = 0;
static int runningLoops = 0;
static bool sendMsgFlag = false;
@@ -209,7 +208,6 @@
drawOffscreenResult(0, 0, quadW, quadH);
int left = 0, right = 0, top = 0, bottom = 0;
- uint width = rsgGetWidth();
uint height = rsgGetHeight();
rsgFontColor(0.9f, 0.9f, 0.95f, 1.0f);
rsgBindFont(gFontSerif);
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs
index 7f10019..0f50828 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs
@@ -52,7 +52,6 @@
fonts[3] = gFontSerif;
fonts[4] = gFontSans;
- uint width = gRenderSurfaceW;
uint height = gRenderSurfaceH;
int left = 0, right = 0, top = 0, bottom = 0;
rsgMeasureText(sampleText, &left, &right, &top, &bottom);
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/ui_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/ui_test.rs
index 5089092..e87db39 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/ui_test.rs
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/ui_test.rs
@@ -143,7 +143,6 @@
float d = fabs(randomGauss()) * gGalaxyRadius * 0.5f + rsRand(64.0f);
float id = d / gGalaxyRadius;
float z = randomGauss() * 0.4f * (1.0f - id);
- float p = -d * ELLIPSE_TWIST;
if (d < gGalaxyRadius * 0.33f) {
part->color.x = (uchar) (220 + id * 35);
@@ -305,7 +304,6 @@
int left = 0, right = 0, top = 0, bottom = 0;
rsgMeasureText(gSampleTextList100[0].item, &left, &right, &top, &bottom);
float textHeight = (float)(top - bottom);
- float textWidth = (float)(right - left);
rs_matrix4x4 matrix;
rsMatrixLoadScale(&matrix, size, size, 1.0);
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/params.rsh b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/params.rsh
index 575794b..00793c0 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/params.rsh
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/params.rsh
@@ -19,7 +19,7 @@
#include "scenegraph_objects.rsh"
//#define DEBUG_PARAMS
-static void debugParam(SgShaderParam *p, SgShaderParamData *pData) {
+static inline void debugParam(SgShaderParam *p, SgShaderParamData *pData) {
rsDebug("____________ Param ____________", p);
printName(pData->paramName);
rsDebug("bufferOffset", p->bufferOffset);
@@ -44,8 +44,7 @@
}
}
-
-static void writeFloatData(float *ptr, const float4 *input, uint32_t vecSize) {
+static inline void writeFloatData(float *ptr, const float4 *input, uint32_t vecSize) {
#ifdef DEBUG_PARAMS
rsDebug("Writing value ", *input);
rsDebug("Writing vec size ", vecSize);
@@ -67,7 +66,7 @@
}
}
-static bool processParam(SgShaderParam *p, SgShaderParamData *pData,
+static inline bool processParam(SgShaderParam *p, SgShaderParamData *pData,
uint8_t *constantBuffer,
const SgCamera *currentCam,
SgFragmentShader *shader) {
@@ -155,7 +154,7 @@
return true;
}
-static void processAllParams(rs_allocation shaderConst,
+static inline void processAllParams(rs_allocation shaderConst,
rs_allocation allParams,
const SgCamera *camera) {
if (rsIsObject(shaderConst)) {
@@ -177,7 +176,7 @@
}
}
-static void processTextureParams(SgFragmentShader *shader) {
+static inline void processTextureParams(SgFragmentShader *shader) {
int numParams = 0;
if (rsIsObject(shader->shaderTextureParams)) {
numParams = rsAllocationGetDimX(shader->shaderTextureParams);
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs
index 8a73dbd..205b2cb 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs
@@ -78,7 +78,7 @@
if (rsIsObject(renderState->pr)) {
rsgBindProgramRaster(renderState->pr);
} else {
- rs_program_raster pr;
+ rs_program_raster pr = {0};
rsgBindProgramRaster(pr);
}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/scenegraph_objects.rsh b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/scenegraph_objects.rsh
index bdca3ab..90ae212 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/scenegraph_objects.rsh
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/scenegraph_objects.rsh
@@ -208,7 +208,7 @@
rs_allocation texture;
} SgTexture;
-static void printName(rs_allocation name) {
+static inline void printName(rs_allocation name) {
if (!rsIsObject(name)) {
rsDebug("no name", 0);
return;
@@ -217,7 +217,7 @@
rsDebug((const char*)rsGetElementAt(name, 0), 0);
}
-static void printCameraInfo(const SgCamera *cam) {
+static inline void printCameraInfo(const SgCamera *cam) {
rsDebug("***** Camera information. ptr:", cam);
printName(cam->name);
const SgTransform *camTransform = (const SgTransform *)rsGetElementAt(cam->transformMatrix, 0);
@@ -233,7 +233,7 @@
rsDebug("View: ", &cam->view);
}
-static void printLightInfo(const SgLight *light) {
+static inline void printLightInfo(const SgLight *light) {
rsDebug("***** Light information. ptr:", light);
printName(light->name);
const SgTransform *lTransform = (const SgTransform *)rsGetElementAt(light->transformMatrix, 0);
@@ -246,7 +246,7 @@
rsDebug("Type: ", light->type);
}
-static void getCameraRay(const SgCamera *cam, int screenX, int screenY, float3 *pnt, float3 *vec) {
+static inline void getCameraRay(const SgCamera *cam, int screenX, int screenY, float3 *pnt, float3 *vec) {
rsDebug("=================================", screenX);
rsDebug("Point X", screenX);
rsDebug("Point Y", screenY);
@@ -290,7 +290,7 @@
*pnt = cam->position.xyz;
}
-static bool intersect(const SgRenderable *obj, float3 pnt, float3 vec) {
+static inline bool intersect(const SgRenderable *obj, float3 pnt, float3 vec) {
// Solving for t^2 + Bt + C = 0
float3 originMinusCenter = pnt - obj->worldBoundingSphere.xyz;
float B = dot(originMinusCenter, vec) * 2.0f;
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/transform.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/transform.rs
index 941b5a8..1d0b5be 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/transform.rs
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/transform.rs
@@ -26,6 +26,7 @@
} ParentData;
//#define DEBUG_TRANSFORMS
+/* Unused function:
static void debugTransform(SgTransform *data, const ParentData *parent) {
rsDebug("****** <Transform> ******", (int)data);
printName(data->name);
@@ -53,6 +54,7 @@
rsDebug("timestamp", data->timestamp);
rsDebug("****** </Transform> ******", (int)data);
}
+*/
static void appendTransformation(int type, float4 data, rs_matrix4x4 *mat) {
rs_matrix4x4 temp;
@@ -119,7 +121,7 @@
}
if (rsIsObject(data->children)) {
- rs_allocation nullAlloc;
+ rs_allocation nullAlloc = {0};
rsForEach(gTransformScript, data->children, nullAlloc, &toChild, sizeof(toChild));
}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rs
index 997a1a7..d94da52 100644
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rs
+++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rs
@@ -41,9 +41,7 @@
gRotate = 0.0f;
}
-static int pos = 50;
static float gRotateY = 120.0f;
-static float3 gLookAt = 0;
static float gZoom = 50.0f;
static void displayLoading() {
if (rsIsObject(gRobotTex) && rsIsObject(gRobotMesh)) {
diff --git a/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/shaderstest.rs b/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/shaderstest.rs
index ae32e3a..735f6b9 100644
--- a/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/shaderstest.rs
+++ b/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/shaderstest.rs
@@ -131,7 +131,6 @@
rs_allocation allMeshes = rsGetAllocation(gMeshes);
int size = rsAllocationGetDimX(allMeshes);
gLookAt = 0.0f;
- float minX, minY, minZ, maxX, maxY, maxZ;
for (int i = 0; i < size; i++) {
MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i);
rsgDrawMesh(info->mMesh);
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index 572fdc9..54a508a 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -320,7 +320,8 @@
BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
parser, mContext, resValue.isFramework());
try {
- return ColorStateList.createFromXml(mContext.getResources(), blockParser);
+ return ColorStateList.createFromXml(mContext.getResources(), blockParser,
+ mContext.getTheme());
} finally {
blockParser.ensurePopped();
}
@@ -444,7 +445,7 @@
@Override
public int getDimensionPixelSize(int index, int defValue) {
try {
- return getDimension(index);
+ return getDimension(index, null);
} catch (RuntimeException e) {
String s = getString(index);
@@ -474,12 +475,12 @@
@Override
public int getLayoutDimension(int index, String name) {
try {
- // this will throw an exception
- return getDimension(index);
+ // this will throw an exception if not found.
+ return getDimension(index, name);
} catch (RuntimeException e) {
if (LayoutInflater_Delegate.sIsInInclude) {
- throw new RuntimeException();
+ throw new RuntimeException("Layout Dimension '" + name + "' not found.");
}
Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
@@ -494,9 +495,13 @@
return getDimensionPixelSize(index, defValue);
}
- private int getDimension(int index) {
+ /** @param name attribute name, used for error reporting. */
+ private int getDimension(int index, @Nullable String name) {
String s = getString(index);
if (s == null) {
+ if (name != null) {
+ throw new RuntimeException("Attribute '" + name + "' not found");
+ }
throw new RuntimeException();
}
// Check if the value is a magic constant that doesn't require a unit.
diff --git a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
index dda8ebb..4226526 100644
--- a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
@@ -82,36 +82,8 @@
builder.mText = text;
builder.mWidths = new float[length];
-
- // compute all possible breakpoints.
- BreakIterator it = BreakIterator.getLineInstance(new ULocale(builder.mLocale));
- it.setText(new Segment(builder.mText, 0, length));
-
- // average word length in english is 5. So, initialize the possible breaks with a guess.
- List<Integer> breaks = new ArrayList<Integer>((int) Math.ceil(length / 5d));
- int loc;
- it.first();
- while ((loc = it.next()) != BreakIterator.DONE) {
- breaks.add(loc);
- }
- LineWidth lineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth);
- TabStops tabStopCalculator = new TabStops(variableTabStops, defaultTabStop);
- List<Primitive> primitives =
- computePrimitives(builder.mText, builder.mWidths, length, breaks);
- BreakStrategy strategy = BreakStrategy.getStrategy(breakStrategy);
- switch (strategy) {
- case GREEDY:
- builder.mLineBreaker =
- new GreedyLineBreaker(primitives, lineWidth, tabStopCalculator);
- break;
- case HIGH_QUALITY:
- // TODO
-// break;
- case BALANCED:
- builder.mLineBreaker = new OptimizingLineBreaker(primitives, lineWidth,
- tabStopCalculator);
- break;
- }
+ builder.mLineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth);
+ builder.mTabStopCalculator = new TabStops(variableTabStops, defaultTabStop);
}
@LayoutlibDelegate
@@ -160,6 +132,37 @@
if (builder == null) {
return 0;
}
+
+ // compute all possible breakpoints.
+ int length = builder.mWidths.length;
+ BreakIterator it = BreakIterator.getLineInstance(new ULocale(builder.mLocale));
+ it.setText(new Segment(builder.mText, 0, length));
+
+ // average word length in english is 5. So, initialize the possible breaks with a guess.
+ List<Integer> breaks = new ArrayList<Integer>((int) Math.ceil(length / 5d));
+ int loc;
+ it.first();
+ while ((loc = it.next()) != BreakIterator.DONE) {
+ breaks.add(loc);
+ }
+
+ List<Primitive> primitives =
+ computePrimitives(builder.mText, builder.mWidths, length, breaks);
+ switch (builder.mBreakStrategy) {
+ case Layout.BREAK_STRATEGY_SIMPLE:
+ builder.mLineBreaker = new GreedyLineBreaker(primitives, builder.mLineWidth,
+ builder.mTabStopCalculator);
+ break;
+ case Layout.BREAK_STRATEGY_HIGH_QUALITY:
+ // TODO
+// break;
+ case Layout.BREAK_STRATEGY_BALANCED:
+ builder.mLineBreaker = new OptimizingLineBreaker(primitives, builder.mLineWidth,
+ builder.mTabStopCalculator);
+ break;
+ default:
+ throw new AssertionError("Unknown break strategy: " + builder.mBreakStrategy);
+ }
builder.mLineBreaker.computeBreaks(recycle);
return recycle.breaks.length;
}
@@ -223,22 +226,8 @@
float[] mWidths;
LineBreaker mLineBreaker;
long mNativeHyphenator;
- }
-
- private enum BreakStrategy {
- GREEDY, HIGH_QUALITY, BALANCED;
-
- static BreakStrategy getStrategy(int strategy) {
- switch (strategy) {
- case 0:
- return GREEDY;
- case 1:
- return HIGH_QUALITY;
- case 2:
- return BALANCED;
- default:
- throw new AssertionError("Unknown break strategy: " + strategy);
- }
- }
+ int mBreakStrategy;
+ LineWidth mLineWidth;
+ TabStops mTabStopCalculator;
}
}
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index 87762a6..32ee9e8 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -53,9 +53,14 @@
*/
private static final String[] sClassPrefixList = {
"android.widget.",
- "android.webkit."
+ "android.webkit.",
+ "android.app."
};
+ public static String[] getClassPrefixList() {
+ return sClassPrefixList;
+ }
+
protected BridgeInflater(LayoutInflater original, Context newContext) {
super(original, newContext);
newContext = getBaseContext(newContext);
diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
index 4072302..27b406a 100644
--- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -181,7 +181,8 @@
// ---- END CHANGES
params = group.generateLayoutParams(attrs);
-
+ } catch (RuntimeException ignored) {
+ // Ignore, just fail over to child attrs.
} finally {
// ---- START CHANGES
sIsInInclude = false;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index eb5f597..59f07a7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -138,8 +138,9 @@
private final Stack<BridgeXmlBlockParser> mParserStack = new Stack<BridgeXmlBlockParser>();
private SharedPreferences mSharedPreferences;
+ private ClassLoader mClassLoader;
- /**
+ /**
* @param projectKey An Object identifying the project. This is used for the cache mechanism.
* @param metrics the {@link DisplayMetrics}.
* @param renderResources the configured resources (both framework and projects) for this
@@ -462,7 +463,21 @@
@Override
public ClassLoader getClassLoader() {
- return this.getClass().getClassLoader();
+ if (mClassLoader == null) {
+ mClassLoader = new ClassLoader(getClass().getClassLoader()) {
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ for (String prefix : BridgeInflater.getClassPrefixList()) {
+ if (name.startsWith(prefix)) {
+ // These are framework classes and should not be loaded from the app.
+ throw new ClassNotFoundException(name + " not found");
+ }
+ }
+ return BridgeContext.this.mLayoutlibCallback.findClass(name);
+ }
+ };
+ }
+ return mClassLoader;
}
@Override
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 2b95488..f3a0d58 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -275,7 +275,6 @@
mContext.getRenderResources().setLogger(null);
}
ParserFactory.setParserFactory(null);
-
}
public static BridgeContext getCurrentContext() {
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index 91be0bd..63115e4 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -124,9 +124,14 @@
if (platformDir != null) {
return platformDir;
}
- // Test if workingDir is platform/frameworks/base/tools/layoutlib. That is, root should be
- // workingDir/../../../../ (4 levels up)
+
+ // Test if workingDir is platform/frameworks/base/tools/layoutlib/bridge.
File currentDir = workingDir;
+ if (currentDir.getName().equalsIgnoreCase("bridge")) {
+ currentDir = currentDir.getParentFile();
+ }
+ // Test if currentDir is platform/frameworks/base/tools/layoutlib. That is, root should be
+ // workingDir/../../../../ (4 levels up)
for (int i = 0; i < 4; i++) {
if (currentDir != null) {
currentDir = currentDir.getParentFile();
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
index aa51c46..c8b2b84 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
@@ -728,7 +728,7 @@
// Check if method needs to replaced by a call to a different method.
- if (ReplaceMethodCallsAdapter.isReplacementNeeded(owner, name, desc)) {
+ if (ReplaceMethodCallsAdapter.isReplacementNeeded(owner, name, desc, mOwnerClass)) {
mReplaceMethodCallClasses.add(mOwnerClass);
}
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
index bd6f070..f6c2626 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -24,9 +24,11 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -72,6 +74,9 @@
/** FQCN Names of classes to refactor. All reference to old-FQCN will be updated to new-FQCN.
* map old-FQCN => new-FQCN */
private final HashMap<String, String> mRefactorClasses;
+ /** Methods to inject. FQCN of class in which method should be injected => runnable that does
+ * the injection. */
+ private final Map<String, ICreateInfo.InjectMethodRunnable> mInjectedMethodsMap;
/**
* Creates a new generator that can generate the output JAR with the stubbed classes.
@@ -83,7 +88,23 @@
public AsmGenerator(Log log, String osDestJar, ICreateInfo createInfo) {
mLog = log;
mOsDestJar = osDestJar;
- mInjectClasses = createInfo.getInjectedClasses();
+ ArrayList<Class<?>> injectedClasses =
+ new ArrayList<Class<?>>(Arrays.asList(createInfo.getInjectedClasses()));
+ // Search for and add anonymous inner classes also.
+ ListIterator<Class<?>> iter = injectedClasses.listIterator();
+ while (iter.hasNext()) {
+ Class<?> clazz = iter.next();
+ try {
+ int i = 1;
+ while(i < 100) {
+ iter.add(Class.forName(clazz.getName() + "$" + i));
+ i++;
+ }
+ } catch (ClassNotFoundException ignored) {
+ // Expected.
+ }
+ }
+ mInjectClasses = injectedClasses.toArray(new Class<?>[0]);
mStubMethods = new HashSet<String>(Arrays.asList(createInfo.getOverriddenMethods()));
// Create the map/set of methods to change to delegates
@@ -165,6 +186,8 @@
}
returnTypes.add(binaryToInternalClassName(className));
}
+
+ mInjectedMethodsMap = createInfo.getInjectedMethodsMap();
}
/**
@@ -285,13 +308,7 @@
* e.g. it returns something like "com/foo/OuterClass$InnerClass1$InnerClass2.class"
*/
private String classToEntryPath(Class<?> clazz) {
- String name = "";
- Class<?> parent;
- while ((parent = clazz.getEnclosingClass()) != null) {
- name = "$" + clazz.getSimpleName() + name;
- clazz = parent;
- }
- return classNameToEntryPath(clazz.getCanonicalName() + name);
+ return classNameToEntryPath(clazz.getName());
}
/**
@@ -337,7 +354,7 @@
ClassVisitor cv = cw;
if (mReplaceMethodCallsClasses.contains(className)) {
- cv = new ReplaceMethodCallsAdapter(cv);
+ cv = new ReplaceMethodCallsAdapter(cv, className);
}
cv = new RefactorClassAdapter(cv, mRefactorClasses);
@@ -345,6 +362,10 @@
cv = new RenameClassAdapter(cv, className, newName);
}
+ String binaryNewName = newName.replace('/', '.');
+ if (mInjectedMethodsMap.keySet().contains(binaryNewName)) {
+ cv = new InjectMethodsAdapter(cv, mInjectedMethodsMap.get(binaryNewName));
+ }
cv = new TransformClassAdapter(mLog, mStubMethods, mDeleteReturns.get(className),
newName, cv, stubNativesOnly);
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 245cd61..27ab5ea 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -26,7 +26,9 @@
import com.android.tools.layoutlib.java.UnsafeByteSequence;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
/**
@@ -105,6 +107,7 @@
return JAVA_PKG_CLASSES;
}
+ @Override
public Set<String> getExcludedClasses() {
String[] refactoredClasses = getJavaPkgClasses();
int count = refactoredClasses.length / 2 + EXCLUDED_CLASSES.length;
@@ -115,6 +118,12 @@
excludedClasses.addAll(Arrays.asList(EXCLUDED_CLASSES));
return excludedClasses;
}
+
+ @Override
+ public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
+ return INJECTED_METHODS;
+ }
+
//-----
/**
@@ -127,6 +136,8 @@
ICreateInfo.class,
CreateInfo.class,
LayoutlibDelegate.class,
+ InjectMethodRunnable.class,
+ InjectMethodRunnables.class,
/* Java package classes */
AutoCloseable.class,
Objects.class,
@@ -286,5 +297,10 @@
private final static String[] DELETE_RETURNS =
new String[] {
null }; // separator, for next class/methods list.
-}
+ private final static Map<String, InjectMethodRunnable> INJECTED_METHODS =
+ new HashMap<String, InjectMethodRunnable>(1) {{
+ put("android.content.Context",
+ InjectMethodRunnables.CONTEXT_GET_FRAMEWORK_CLASS_LOADER);
+ }};
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
index e49a668..54b1fe6 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
@@ -16,6 +16,9 @@
package com.android.tools.layoutlib.create;
+import org.objectweb.asm.ClassVisitor;
+
+import java.util.Map;
import java.util.Set;
/**
@@ -27,33 +30,33 @@
* Returns the list of class from layoutlib_create to inject in layoutlib.
* The list can be empty but must not be null.
*/
- public abstract Class<?>[] getInjectedClasses();
+ Class<?>[] getInjectedClasses();
/**
* Returns the list of methods to rewrite as delegates.
* The list can be empty but must not be null.
*/
- public abstract String[] getDelegateMethods();
+ String[] getDelegateMethods();
/**
* Returns the list of classes on which to delegate all native methods.
* The list can be empty but must not be null.
*/
- public abstract String[] getDelegateClassNatives();
+ String[] getDelegateClassNatives();
/**
* Returns The list of methods to stub out. Each entry must be in the form
* "package.package.OuterClass$InnerClass#MethodName".
* The list can be empty but must not be null.
*/
- public abstract String[] getOverriddenMethods();
+ String[] getOverriddenMethods();
/**
* Returns the list of classes to rename, must be an even list: the binary FQCN
* of class to replace followed by the new FQCN.
* The list can be empty but must not be null.
*/
- public abstract String[] getRenamedClasses();
+ String[] getRenamedClasses();
/**
* Returns the list of classes for which the methods returning them should be deleted.
@@ -62,7 +65,7 @@
* the methods to delete.
* The list can be empty but must not be null.
*/
- public abstract String[] getDeleteReturns();
+ String[] getDeleteReturns();
/**
* Returns the list of classes to refactor, must be an even list: the
@@ -70,7 +73,24 @@
* to the old class should be updated to the new class.
* The list can be empty but must not be null.
*/
- public abstract String[] getJavaPkgClasses();
+ String[] getJavaPkgClasses();
- public abstract Set<String> getExcludedClasses();
+ Set<String> getExcludedClasses();
+
+ /**
+ * Returns a map from binary FQCN className to {@link InjectMethodRunnable} which will be
+ * called to inject methods into a class.
+ * Can be empty but must not be null.
+ */
+ Map<String, InjectMethodRunnable> getInjectedMethodsMap();
+
+ abstract class InjectMethodRunnable {
+ /**
+ * @param cv Must be {@link ClassVisitor}. However, the param type is object so that when
+ * loading the class, ClassVisitor is not loaded. This is because when injecting
+ * CreateInfo in LayoutLib (see {@link #getInjectedClasses()}, we don't want to inject
+ * asm classes also, but still keep CreateInfo loadable.
+ */
+ public abstract void generateMethods(Object cv);
+ }
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/InjectMethodRunnables.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/InjectMethodRunnables.java
new file mode 100644
index 0000000..37fc096
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/InjectMethodRunnables.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import com.android.tools.layoutlib.create.ICreateInfo.InjectMethodRunnable;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.ARETURN;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+
+public class InjectMethodRunnables {
+ public static final ICreateInfo.InjectMethodRunnable CONTEXT_GET_FRAMEWORK_CLASS_LOADER
+ = new InjectMethodRunnable() {
+ @Override
+ public void generateMethods(Object classVisitor) {
+ assert classVisitor instanceof ClassVisitor;
+ ClassVisitor cv = (ClassVisitor) classVisitor;
+ // generated by compiling the class:
+ // class foo { public ClassLoader getFrameworkClassLoader() { return getClass().getClassLoader(); } }
+ // and then running ASMifier on it:
+ // java -classpath asm-debug-all-5.0.2.jar:. org.objectweb.asm.util.ASMifier foo
+ MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "getFrameworkClassLoader",
+ "()Ljava/lang/ClassLoader;", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass",
+ "()Ljava/lang/Class;");
+ mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getClassLoader",
+ "()Ljava/lang/ClassLoader;");
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+ // generated code ends.
+ }
+ };
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/InjectMethodsAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/InjectMethodsAdapter.java
new file mode 100644
index 0000000..ea2b9c9
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/InjectMethodsAdapter.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import com.android.tools.layoutlib.create.ICreateInfo.InjectMethodRunnable;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Injects methods into some classes.
+ */
+public class InjectMethodsAdapter extends ClassVisitor {
+
+ private final ICreateInfo.InjectMethodRunnable mRunnable;
+
+ public InjectMethodsAdapter(ClassVisitor cv, InjectMethodRunnable runnable) {
+ super(Opcodes.ASM4, cv);
+ mRunnable = runnable;
+ }
+
+ @Override
+ public void visitEnd() {
+ mRunnable.generateMethods(this);
+ super.visitEnd();
+ }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
index 384d8ca..4369148 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
@@ -62,14 +62,13 @@
// Case 1: java.lang.System.arraycopy()
METHOD_REPLACERS.add(new MethodReplacer() {
@Override
- public boolean isNeeded(String owner, String name, String desc) {
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
return JAVA_LANG_SYSTEM.equals(owner) && "arraycopy".equals(name) &&
ARRAYCOPY_DESCRIPTORS.contains(desc);
}
@Override
public void replace(MethodInformation mi) {
- assert isNeeded(mi.owner, mi.name, mi.desc);
mi.desc = "(Ljava/lang/Object;ILjava/lang/Object;II)V";
}
});
@@ -81,14 +80,13 @@
Type.getMethodDescriptor(STRING, Type.getType(Locale.class));
@Override
- public boolean isNeeded(String owner, String name, String desc) {
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
return JAVA_LOCALE_CLASS.equals(owner) && "()Ljava/lang/String;".equals(desc) &&
("toLanguageTag".equals(name) || "getScript".equals(name));
}
@Override
public void replace(MethodInformation mi) {
- assert isNeeded(mi.owner, mi.name, mi.desc);
mi.opcode = Opcodes.INVOKESTATIC;
mi.owner = ANDROID_LOCALE_CLASS;
mi.desc = LOCALE_TO_STRING;
@@ -103,7 +101,7 @@
Type.getType(Locale.class), STRING);
@Override
- public boolean isNeeded(String owner, String name, String desc) {
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
return JAVA_LOCALE_CLASS.equals(owner) &&
("adjustLanguageCode".equals(name) && desc.equals(STRING_TO_STRING) ||
"forLanguageTag".equals(name) && desc.equals(STRING_TO_LOCALE));
@@ -111,7 +109,6 @@
@Override
public void replace(MethodInformation mi) {
- assert isNeeded(mi.owner, mi.name, mi.desc);
mi.owner = ANDROID_LOCALE_CLASS;
}
});
@@ -119,14 +116,13 @@
// Case 4: java.lang.System.log?()
METHOD_REPLACERS.add(new MethodReplacer() {
@Override
- public boolean isNeeded(String owner, String name, String desc) {
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
return JAVA_LANG_SYSTEM.equals(owner) && name.length() == 4
&& name.startsWith("log");
}
@Override
public void replace(MethodInformation mi) {
- assert isNeeded(mi.owner, mi.name, mi.desc);
assert mi.desc.equals("(Ljava/lang/String;Ljava/lang/Throwable;)V")
|| mi.desc.equals("(Ljava/lang/String;)V");
mi.name = "log";
@@ -142,7 +138,7 @@
private final String LINKED_HASH_MAP = Type.getInternalName(LinkedHashMap.class);
@Override
- public boolean isNeeded(String owner, String name, String desc) {
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
return LINKED_HASH_MAP.equals(owner) &&
"eldest".equals(name) &&
VOID_TO_MAP_ENTRY.equals(desc);
@@ -150,26 +146,64 @@
@Override
public void replace(MethodInformation mi) {
- assert isNeeded(mi.owner, mi.name, mi.desc);
mi.opcode = Opcodes.INVOKESTATIC;
mi.owner = Type.getInternalName(LinkedHashMap_Delegate.class);
mi.desc = Type.getMethodDescriptor(
Type.getType(Map.Entry.class), Type.getType(LinkedHashMap.class));
}
});
+
+ // Case 6: android.content.Context.getClassLoader() in LayoutInflater
+ METHOD_REPLACERS.add(new MethodReplacer() {
+ // When LayoutInflater asks for a class loader, we must return the class loader that
+ // cannot return app's custom views/classes. This is so that in case of any failure
+ // or exception when instantiating the views, the IDE can replace it with a mock view
+ // and have proper error handling. However, if a custom view asks for the class
+ // loader, we must return a class loader that can find app's custom views as well.
+ // Thus, we rewrite the call to get class loader in LayoutInflater to
+ // getFrameworkClassLoader and inject a new method in Context. This leaves the normal
+ // method: Context.getClassLoader() free to be used by the apps.
+ private final String VOID_TO_CLASS_LOADER =
+ Type.getMethodDescriptor(Type.getType(ClassLoader.class));
+
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return owner.equals("android/content/Context") &&
+ sourceClass.equals("android/view/LayoutInflater") &&
+ name.equals("getClassLoader") &&
+ desc.equals(VOID_TO_CLASS_LOADER);
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.name = "getFrameworkClassLoader";
+ }
+ });
}
- public static boolean isReplacementNeeded(String owner, String name, String desc) {
+ /**
+ * If a method some.package.Class.Method(args) is called from some.other.Class,
+ * @param owner some/package/Class
+ * @param name Method
+ * @param desc (args)returnType
+ * @param sourceClass some/other/Class
+ * @return if the method invocation needs to be replaced by some other class.
+ */
+ public static boolean isReplacementNeeded(String owner, String name, String desc,
+ String sourceClass) {
for (MethodReplacer replacer : METHOD_REPLACERS) {
- if (replacer.isNeeded(owner, name, desc)) {
+ if (replacer.isNeeded(owner, name, desc, sourceClass)) {
return true;
}
}
return false;
}
- public ReplaceMethodCallsAdapter(ClassVisitor cv) {
+ private final String mOriginalClassName;
+
+ public ReplaceMethodCallsAdapter(ClassVisitor cv, String originalClassName) {
super(Opcodes.ASM4, cv);
+ mOriginalClassName = originalClassName;
}
@Override
@@ -187,7 +221,7 @@
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
for (MethodReplacer replacer : METHOD_REPLACERS) {
- if (replacer.isNeeded(owner, name, desc)) {
+ if (replacer.isNeeded(owner, name, desc, mOriginalClassName)) {
MethodInformation mi = new MethodInformation(opcode, owner, name, desc);
replacer.replace(mi);
opcode = mi.opcode;
@@ -216,13 +250,12 @@
}
private interface MethodReplacer {
- public boolean isNeeded(String owner, String name, String desc);
+ boolean isNeeded(String owner, String name, String desc, String sourceClass);
/**
* Updates the MethodInformation with the new values of the method attributes -
* opcode, owner, name and desc.
- *
*/
- public void replace(MethodInformation mi);
+ void replace(MethodInformation mi);
}
}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
index cf91386..2c21470 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -19,7 +19,9 @@
import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.After;
@@ -32,13 +34,17 @@
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -130,6 +136,11 @@
// methods deleted from their return type.
return new String[0];
}
+
+ @Override
+ public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
+ return new HashMap<String, InjectMethodRunnable>(0);
+ }
};
AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci);
@@ -200,6 +211,11 @@
// methods deleted from their return type.
return new String[0];
}
+
+ @Override
+ public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
+ return new HashMap<String, InjectMethodRunnable>(0);
+ }
};
AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci);
@@ -278,6 +294,11 @@
// methods deleted from their return type.
return new String[0];
}
+
+ @Override
+ public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
+ return new HashMap<String, InjectMethodRunnable>(0);
+ }
};
AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci);
@@ -303,6 +324,118 @@
filesFound.keySet().toArray());
}
+ @Test
+ public void testMethodInjection() throws IOException, LogAbortException,
+ ClassNotFoundException, IllegalAccessException, InstantiationException,
+ NoSuchMethodException, InvocationTargetException {
+ ICreateInfo ci = new ICreateInfo() {
+ @Override
+ public Class<?>[] getInjectedClasses() {
+ return new Class<?>[0];
+ }
+
+ @Override
+ public String[] getDelegateMethods() {
+ return new String[0];
+ }
+
+ @Override
+ public String[] getDelegateClassNatives() {
+ return new String[0];
+ }
+
+ @Override
+ public String[] getOverriddenMethods() {
+ // methods to force override
+ return new String[0];
+ }
+
+ @Override
+ public String[] getRenamedClasses() {
+ // classes to rename (so that we can replace them)
+ return new String[0];
+ }
+
+ @Override
+ public String[] getJavaPkgClasses() {
+ // classes to refactor (so that we can replace them)
+ return new String[0];
+ }
+
+ @Override
+ public Set<String> getExcludedClasses() {
+ return new HashSet<String>(0);
+ }
+
+ @Override
+ public String[] getDeleteReturns() {
+ // methods deleted from their return type.
+ return new String[0];
+ }
+
+ @Override
+ public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
+ HashMap<String, InjectMethodRunnable> map =
+ new HashMap<String, InjectMethodRunnable>(1);
+ map.put("mock_android.util.EmptyArray",
+ InjectMethodRunnables.CONTEXT_GET_FRAMEWORK_CLASS_LOADER);
+ return map;
+ }
+ };
+
+ AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci);
+ AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath, agen,
+ null, // derived from
+ new String[] { // include classes
+ "**"
+ },
+ ci.getExcludedClasses(),
+ new String[] { /* include files */
+ "mock_android/data/data*"
+ });
+ aa.analyze();
+ agen.generate();
+ Map<String, ClassReader> output = new TreeMap<String, ClassReader>();
+ Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+ parseZip(mOsDestJar, output, filesFound);
+ final String modifiedClass = "mock_android.util.EmptyArray";
+ final String modifiedClassPath = modifiedClass.replace('.', '/').concat(".class");
+ ZipFile zipFile = new ZipFile(mOsDestJar);
+ ZipEntry entry = zipFile.getEntry(modifiedClassPath);
+ assertNotNull(entry);
+ final byte[] bytes;
+ final InputStream inputStream = zipFile.getInputStream(entry);
+ try {
+ bytes = getByteArray(inputStream);
+ } finally {
+ inputStream.close();
+ }
+ ClassLoader classLoader = new ClassLoader(getClass().getClassLoader()) {
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ if (name.equals(modifiedClass)) {
+ return defineClass(null, bytes, 0, bytes.length);
+ }
+ throw new ClassNotFoundException(name + " not found.");
+ }
+ };
+ Class<?> emptyArrayClass = classLoader.loadClass(modifiedClass);
+ Object emptyArrayInstance = emptyArrayClass.newInstance();
+ Method method = emptyArrayClass.getMethod("getFrameworkClassLoader");
+ Object cl = method.invoke(emptyArrayInstance);
+ assertEquals(classLoader, cl);
+ }
+
+ private static byte[] getByteArray(InputStream stream) throws IOException {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int read;
+ while ((read = stream.read(buffer, 0, buffer.length)) > -1) {
+ bos.write(buffer, 0, read);
+ }
+ return bos.toByteArray();
+ }
+
private void parseZip(String jarPath,
Map<String, ClassReader> classes,
Map<String, InputStream> filesFound) throws IOException {