Merge "Rebind backup transports only when clearly needed" into mnc-dev
diff --git a/Android.mk b/Android.mk
index eb6e7b5..146afe0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -84,6 +84,7 @@
core/java/android/app/job/IJobScheduler.aidl \
core/java/android/app/job/IJobService.aidl \
core/java/android/app/ITransientNotification.aidl \
+ core/java/android/app/IUidObserver.aidl \
core/java/android/app/IUiAutomationConnection.aidl \
core/java/android/app/IUiModeManager.aidl \
core/java/android/app/IUserSwitchObserver.aidl \
diff --git a/api/current.txt b/api/current.txt
index 50147d8..80c4dc8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7160,6 +7160,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);
@@ -28891,7 +28892,6 @@
field public static final java.lang.String SERVICE_META_DATA = "android.voice_interaction";
field public static final int START_SOURCE_ASSIST_GESTURE = 4; // 0x4
field public static final int START_WITH_ASSIST = 1; // 0x1
- field public static final int START_WITH_SCREENSHOT = 2; // 0x2
}
public abstract class VoiceInteractionSession implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback {
@@ -28917,7 +28917,6 @@
method public void onDestroy();
method public boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]);
method public void onHandleAssist(android.os.Bundle);
- method public void onHandleScreenshot(android.graphics.Bitmap);
method public void onHide();
method public boolean onKeyDown(int, android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
@@ -30325,6 +30324,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);
@@ -34445,6 +34445,8 @@
method public abstract void setTitle(int);
method public void setTitleOptionalHint(boolean);
method public void setType(int);
+ method public void snooze(int);
+ field public static final int SNOOZE_TIME_DEFAULT;
field public static final int TYPE_FLOATING = 1; // 0x1
field public static final int TYPE_PRIMARY = 0; // 0x0
}
@@ -36509,6 +36511,7 @@
public class ViewConfiguration {
ctor public deprecated ViewConfiguration();
method public static android.view.ViewConfiguration get(android.content.Context);
+ method public static int getDefaultActionModeSnoozeTime();
method public static int getDoubleTapTimeout();
method public static deprecated int getEdgeSlop();
method public static deprecated int getFadingEdgeLength();
@@ -36912,6 +36915,7 @@
method public abstract void setText(java.lang.CharSequence);
method public abstract void setText(java.lang.CharSequence, int, int);
method public abstract void setTextPaint(android.text.TextPaint);
+ method public abstract void setTextStyle(int, int, int, int);
method public abstract void setVisibility(int);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 26466ed..6c4f278 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -31021,7 +31021,6 @@
field public static final java.lang.String SERVICE_META_DATA = "android.voice_interaction";
field public static final int START_SOURCE_ASSIST_GESTURE = 4; // 0x4
field public static final int START_WITH_ASSIST = 1; // 0x1
- field public static final int START_WITH_SCREENSHOT = 2; // 0x2
}
public abstract class VoiceInteractionSession implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback {
@@ -31047,7 +31046,6 @@
method public void onDestroy();
method public boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]);
method public void onHandleAssist(android.os.Bundle);
- method public void onHandleScreenshot(android.graphics.Bitmap);
method public void onHide();
method public boolean onKeyDown(int, android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
@@ -32511,6 +32509,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);
@@ -32652,6 +32651,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();
@@ -32667,7 +32667,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);
@@ -36708,6 +36707,8 @@
method public abstract void setTitle(int);
method public void setTitleOptionalHint(boolean);
method public void setType(int);
+ method public void snooze(int);
+ field public static final int SNOOZE_TIME_DEFAULT;
field public static final int TYPE_FLOATING = 1; // 0x1
field public static final int TYPE_PRIMARY = 0; // 0x0
}
@@ -38772,6 +38773,7 @@
public class ViewConfiguration {
ctor public deprecated ViewConfiguration();
method public static android.view.ViewConfiguration get(android.content.Context);
+ method public static int getDefaultActionModeSnoozeTime();
method public static int getDoubleTapTimeout();
method public static deprecated int getEdgeSlop();
method public static deprecated int getFadingEdgeLength();
@@ -39175,6 +39177,7 @@
method public abstract void setText(java.lang.CharSequence);
method public abstract void setText(java.lang.CharSequence, int, int);
method public abstract void setTextPaint(android.text.TextPaint);
+ method public abstract void setTextStyle(int, int, int, int);
method public abstract void setVisibility(int);
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 15cfbcd..cdf15e1 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1970,6 +1970,22 @@
return true;
}
+ case REGISTER_UID_OBSERVER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IUidObserver observer = IUidObserver.Stub.asInterface(
+ data.readStrongBinder());
+ registerUidObserver(observer);
+ return true;
+ }
+
+ case UNREGISTER_UID_OBSERVER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IUidObserver observer = IUidObserver.Stub.asInterface(
+ data.readStrongBinder());
+ unregisterUidObserver(observer);
+ return true;
+ }
+
case GET_PACKAGE_ASK_SCREEN_COMPAT_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
@@ -5077,6 +5093,28 @@
reply.recycle();
}
+ public void registerUidObserver(IUidObserver observer) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+ mRemote.transact(REGISTER_UID_OBSERVER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ public void unregisterUidObserver(IUidObserver observer) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+ mRemote.transact(UNREGISTER_UID_OBSERVER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5dcbe37..cb436b5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4585,7 +4585,7 @@
// crash if we can't get it.
IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
try {
- final ProxyInfo proxyInfo = service.getDefaultProxy();
+ final ProxyInfo proxyInfo = service.getProxyForNetwork(null);
Proxy.setHttpProxySystemProperty(proxyInfo);
} catch (RemoteException e) {}
}
diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java
index a06bc31..b703b0e 100644
--- a/core/java/android/app/AssistStructure.java
+++ b/core/java/android/app/AssistStructure.java
@@ -635,6 +635,15 @@
}
@Override
+ public void setTextStyle(int size, int fgColor, int bgColor, int style) {
+ ViewNodeText t = getNodeText();
+ t.mTextColor = fgColor;
+ t.mTextBackgroundColor = bgColor;
+ t.mTextSize = size;
+ t.mTextStyle = style;
+ }
+
+ @Override
public void setHint(CharSequence hint) {
getNodeText().mHint = hint != null ? hint.toString() : null;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 4ba1055..310c5ef 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -387,6 +387,9 @@
public void registerProcessObserver(IProcessObserver observer) throws RemoteException;
public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException;
+ public void registerUidObserver(IUidObserver observer) throws RemoteException;
+ public void unregisterUidObserver(IUidObserver observer) throws RemoteException;
+
public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException;
public boolean isIntentSenderAnActivity(IIntentSender sender) throws RemoteException;
@@ -844,4 +847,6 @@
int UPDATE_DEVICE_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+295;
int UPDATE_PREFERRED_SETUP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+296;
int KEYGUARD_GOING_AWAY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+297;
+ int REGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+298;
+ int UNREGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+299;
}
diff --git a/core/java/android/app/IUidObserver.aidl b/core/java/android/app/IUidObserver.aidl
new file mode 100644
index 0000000..308cb94
--- /dev/null
+++ b/core/java/android/app/IUidObserver.aidl
@@ -0,0 +1,23 @@
+/*
+ * 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.app;
+
+/** {@hide} */
+oneway interface IUidObserver {
+ void onUidStateChanged(int uid, int procState);
+ void onUidGone(int uid);
+}
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 4f81f98..eb6166a 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -333,19 +333,25 @@
public void setBluetoothTethering(boolean value) {
if (DBG) log("setBluetoothTethering(" + value + ")");
- try {
- mPanService.setBluetoothTethering(value);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+
+ if (mPanService != null && isEnabled()) {
+ try {
+ mPanService.setBluetoothTethering(value);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ }
}
}
public boolean isTetheringOn() {
if (VDBG) log("isTetheringOn()");
- try {
- return mPanService.isTetheringOn();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+
+ if (mPanService != null && isEnabled()) {
+ try {
+ return mPanService.isTetheringOn();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ }
}
return false;
}
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/camera2/CameraAccessException.java b/core/java/android/hardware/camera2/CameraAccessException.java
index 84e9912..933ce0d 100644
--- a/core/java/android/hardware/camera2/CameraAccessException.java
+++ b/core/java/android/hardware/camera2/CameraAccessException.java
@@ -16,8 +16,13 @@
package android.hardware.camera2;
+import android.annotation.NonNull;
+import android.annotation.IntDef;
import android.util.AndroidException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* <p><code>CameraAccessException</code> is thrown if a camera device could not
* be queried or opened by the {@link CameraManager}, or if the connection to an
@@ -76,6 +81,16 @@
*/
public static final int CAMERA_DEPRECATED_HAL = 1000;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ {CAMERA_IN_USE,
+ MAX_CAMERAS_IN_USE,
+ CAMERA_DISABLED,
+ CAMERA_DISCONNECTED,
+ CAMERA_ERROR})
+ public @interface AccessError {};
+
// Make the eclipse warning about serializable exceptions go away
private static final long serialVersionUID = 5630338637471475675L; // randomly generated
@@ -88,26 +103,27 @@
* @see #CAMERA_DISCONNECTED
* @see #CAMERA_ERROR
*/
+ @AccessError
public final int getReason() {
return mReason;
}
- public CameraAccessException(int problem) {
+ public CameraAccessException(@AccessError int problem) {
super(getDefaultMessage(problem));
mReason = problem;
}
- public CameraAccessException(int problem, String message) {
+ public CameraAccessException(@AccessError int problem, String message) {
super(message);
mReason = problem;
}
- public CameraAccessException(int problem, String message, Throwable cause) {
+ public CameraAccessException(@AccessError int problem, String message, Throwable cause) {
super(message, cause);
mReason = problem;
}
- public CameraAccessException(int problem, Throwable cause) {
+ public CameraAccessException(@AccessError int problem, Throwable cause) {
super(getDefaultMessage(problem), cause);
mReason = problem;
}
@@ -115,7 +131,7 @@
/**
* @hide
*/
- public static String getDefaultMessage(int problem) {
+ public static String getDefaultMessage(@AccessError int problem) {
switch (problem) {
case CAMERA_IN_USE:
return "The camera device is in use already";
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index c6f622c..b3e7cfc 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -16,6 +16,8 @@
package android.hardware.camera2;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Handler;
import android.view.Surface;
@@ -73,6 +75,7 @@
/**
* Get the camera device that this session is created for.
*/
+ @NonNull
public abstract CameraDevice getDevice();
/**
@@ -133,7 +136,7 @@
*
* @see StateCallback#onSurfacePrepared
*/
- public abstract void prepare(Surface surface) throws CameraAccessException;
+ public abstract void prepare(@NonNull Surface surface) throws CameraAccessException;
/**
* <p>Submit a request for an image to be captured by the camera device.</p>
@@ -194,7 +197,8 @@
* @see #abortCaptures
* @see CameraDevice#createReprocessableCaptureSession
*/
- public abstract int capture(CaptureRequest request, CaptureCallback listener, Handler handler)
+ public abstract int capture(@NonNull CaptureRequest request,
+ @Nullable CaptureCallback listener, @Nullable Handler handler)
throws CameraAccessException;
/**
@@ -252,8 +256,9 @@
* @see #setRepeatingBurst
* @see #abortCaptures
*/
- public abstract int captureBurst(List<CaptureRequest> requests, CaptureCallback listener,
- Handler handler) throws CameraAccessException;
+ public abstract int captureBurst(@NonNull List<CaptureRequest> requests,
+ @Nullable CaptureCallback listener, @Nullable Handler handler)
+ throws CameraAccessException;
/**
* Request endlessly repeating capture of images by this capture session.
@@ -318,8 +323,9 @@
* @see #stopRepeating
* @see #abortCaptures
*/
- public abstract int setRepeatingRequest(CaptureRequest request, CaptureCallback listener,
- Handler handler) throws CameraAccessException;
+ public abstract int setRepeatingRequest(@NonNull CaptureRequest request,
+ @Nullable CaptureCallback listener, @Nullable Handler handler)
+ throws CameraAccessException;
/**
* <p>Request endlessly repeating capture of a sequence of images by this
@@ -389,8 +395,9 @@
* @see #stopRepeating
* @see #abortCaptures
*/
- public abstract int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback listener,
- Handler handler) throws CameraAccessException;
+ public abstract int setRepeatingBurst(@NonNull List<CaptureRequest> requests,
+ @Nullable CaptureCallback listener, @Nullable Handler handler)
+ throws CameraAccessException;
/**
* <p>Cancel any ongoing repeating capture set by either
@@ -478,6 +485,7 @@
* @see android.media.ImageWriter
* @see android.media.ImageReader
*/
+ @Nullable
public abstract Surface getInputSurface();
/**
@@ -525,7 +533,7 @@
*
* @param session the session returned by {@link CameraDevice#createCaptureSession}
*/
- public abstract void onConfigured(CameraCaptureSession session);
+ public abstract void onConfigured(@NonNull CameraCaptureSession session);
/**
* This method is called if the session cannot be configured as requested.
@@ -540,7 +548,7 @@
*
* @param session the session returned by {@link CameraDevice#createCaptureSession}
*/
- public abstract void onConfigureFailed(CameraCaptureSession session);
+ public abstract void onConfigureFailed(@NonNull CameraCaptureSession session);
/**
* This method is called every time the session has no more capture requests to process.
@@ -555,7 +563,7 @@
* @param session the session returned by {@link CameraDevice#createCaptureSession}
*
*/
- public void onReady(CameraCaptureSession session) {
+ public void onReady(@NonNull CameraCaptureSession session) {
// default empty implementation
}
@@ -571,7 +579,7 @@
*
* @param session the session returned by {@link CameraDevice#createCaptureSession}
*/
- public void onActive(CameraCaptureSession session) {
+ public void onActive(@NonNull CameraCaptureSession session) {
// default empty implementation
}
@@ -589,7 +597,7 @@
*
* @param session the session returned by {@link CameraDevice#createCaptureSession}
*/
- public void onClosed(CameraCaptureSession session) {
+ public void onClosed(@NonNull CameraCaptureSession session) {
// default empty implementation
}
@@ -608,7 +616,8 @@
* @param session the session returned by {@link CameraDevice#createCaptureSession}
* @param surface the Surface that was used with the {@link #prepare} call.
*/
- public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
+ public void onSurfacePrepared(@NonNull CameraCaptureSession session,
+ @NonNull Surface surface) {
// default empty implementation
}
}
@@ -675,8 +684,8 @@
*
* @see android.media.MediaActionSound
*/
- public void onCaptureStarted(CameraCaptureSession session,
- CaptureRequest request, long timestamp, long frameNumber) {
+ public void onCaptureStarted(@NonNull CameraCaptureSession session,
+ @NonNull CaptureRequest request, long timestamp, long frameNumber) {
// Temporary trampoline for API change transition
onCaptureStarted(session, request, timestamp);
}
@@ -756,8 +765,8 @@
* @see #setRepeatingRequest
* @see #setRepeatingBurst
*/
- public void onCaptureProgressed(CameraCaptureSession session,
- CaptureRequest request, CaptureResult partialResult) {
+ public void onCaptureProgressed(@NonNull CameraCaptureSession session,
+ @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
// default empty implementation
}
@@ -785,8 +794,8 @@
* @see #setRepeatingRequest
* @see #setRepeatingBurst
*/
- public void onCaptureCompleted(CameraCaptureSession session,
- CaptureRequest request, TotalCaptureResult result) {
+ public void onCaptureCompleted(@NonNull CameraCaptureSession session,
+ @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
// default empty implementation
}
@@ -814,8 +823,8 @@
* @see #setRepeatingRequest
* @see #setRepeatingBurst
*/
- public void onCaptureFailed(CameraCaptureSession session,
- CaptureRequest request, CaptureFailure failure) {
+ public void onCaptureFailed(@NonNull CameraCaptureSession session,
+ @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
// default empty implementation
}
@@ -844,7 +853,7 @@
* @see CaptureFailure#getSequenceId()
* @see #onCaptureSequenceAborted
*/
- public void onCaptureSequenceCompleted(CameraCaptureSession session,
+ public void onCaptureSequenceCompleted(@NonNull CameraCaptureSession session,
int sequenceId, long frameNumber) {
// default empty implementation
}
@@ -873,7 +882,7 @@
* @see CaptureFailure#getSequenceId()
* @see #onCaptureSequenceCompleted
*/
- public void onCaptureSequenceAborted(CameraCaptureSession session,
+ public void onCaptureSequenceAborted(@NonNull CameraCaptureSession session,
int sequenceId) {
// default empty implementation
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index d3b63f9..c2a6a44 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -16,6 +16,8 @@
package android.hardware.camera2;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
@@ -91,6 +93,7 @@
*
* @return String representation of the key name
*/
+ @NonNull
public String getName() {
return mKey.getName();
}
@@ -166,6 +169,7 @@
* @param key The characteristics field to read.
* @return The value of that key, or {@code null} if the field is not set.
*/
+ @Nullable
public <T> T get(Key<T> key) {
return mProperties.get(key);
}
@@ -194,6 +198,7 @@
/**
* {@inheritDoc}
*/
+ @NonNull
@Override
public List<Key<?>> getKeys() {
// List of keys is immutable; cache the results after we calculate them
@@ -227,6 +232,7 @@
* @return List of keys supported by this CameraDevice for CaptureRequests.
*/
@SuppressWarnings({"unchecked"})
+ @NonNull
public List<CaptureRequest.Key<?>> getAvailableCaptureRequestKeys() {
if (mAvailableRequestKeys == null) {
Object crKey = CaptureRequest.Key.class;
@@ -258,6 +264,7 @@
* @return List of keys supported by this CameraDevice for CaptureResults.
*/
@SuppressWarnings({"unchecked"})
+ @NonNull
public List<CaptureResult.Key<?>> getAvailableCaptureResultKeys() {
if (mAvailableResultKeys == null) {
Object crKey = CaptureResult.Key.class;
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index dad4fb6..d02f349 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -16,6 +16,9 @@
package android.hardware.camera2;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.IntDef;
import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.params.OutputConfiguration;
@@ -23,6 +26,8 @@
import android.view.Surface;
import java.util.List;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* <p>The CameraDevice class is a representation of a single camera connected to an
@@ -124,6 +129,17 @@
*/
public static final int TEMPLATE_MANUAL = 6;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ {TEMPLATE_PREVIEW,
+ TEMPLATE_STILL_CAPTURE,
+ TEMPLATE_RECORD,
+ TEMPLATE_VIDEO_SNAPSHOT,
+ TEMPLATE_ZERO_SHUTTER_LAG,
+ TEMPLATE_MANUAL })
+ public @interface RequestTemplate {};
+
/**
* Get the ID of this camera device.
*
@@ -142,6 +158,7 @@
* @see CameraManager#getCameraCharacteristics
* @see CameraManager#getCameraIdList
*/
+ @NonNull
public abstract String getId();
/**
@@ -391,8 +408,8 @@
* @see StreamConfigurationMap#getOutputSizes(int)
* @see StreamConfigurationMap#getOutputSizes(Class)
*/
- public abstract void createCaptureSession(List<Surface> outputs,
- CameraCaptureSession.StateCallback callback, Handler handler)
+ public abstract void createCaptureSession(@NonNull List<Surface> outputs,
+ @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler)
throws CameraAccessException;
/**
@@ -560,8 +577,9 @@
* @see android.media.ImageWriter
* @see android.media.ImageReader
*/
- public abstract void createReprocessableCaptureSession(InputConfiguration inputConfig,
- List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)
+ public abstract void createReprocessableCaptureSession(@NonNull InputConfiguration inputConfig,
+ @NonNull List<Surface> outputs, @NonNull CameraCaptureSession.StateCallback callback,
+ @Nullable Handler handler)
throws CameraAccessException;
/**
@@ -591,7 +609,8 @@
* @see #TEMPLATE_VIDEO_SNAPSHOT
* @see #TEMPLATE_MANUAL
*/
- public abstract CaptureRequest.Builder createCaptureRequest(int templateType)
+ @NonNull
+ public abstract CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType)
throws CameraAccessException;
/**
@@ -620,8 +639,9 @@
* @see CameraDevice#createReprocessableCaptureSession
* @see android.media.ImageWriter
*/
+ @NonNull
public abstract CaptureRequest.Builder createReprocessCaptureRequest(
- TotalCaptureResult inputResult) throws CameraAccessException;
+ @NonNull TotalCaptureResult inputResult) throws CameraAccessException;
/**
* Close the connection to this camera device as quickly as possible.
@@ -727,6 +747,16 @@
*/
public static final int ERROR_CAMERA_SERVICE = 5;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ {ERROR_CAMERA_IN_USE,
+ ERROR_MAX_CAMERAS_IN_USE,
+ ERROR_CAMERA_DISABLED,
+ ERROR_CAMERA_DEVICE,
+ ERROR_CAMERA_SERVICE })
+ public @interface ErrorCode {};
+
/**
* The method called when a camera device has finished opening.
*
@@ -736,7 +766,7 @@
*
* @param camera the camera device that has become opened
*/
- public abstract void onOpened(CameraDevice camera); // Must implement
+ public abstract void onOpened(@NonNull CameraDevice camera); // Must implement
/**
* The method called when a camera device has been closed with
@@ -749,7 +779,7 @@
*
* @param camera the camera device that has become closed
*/
- public void onClosed(CameraDevice camera) {
+ public void onClosed(@NonNull CameraDevice camera) {
// Default empty implementation
}
@@ -781,7 +811,7 @@
*
* @param camera the device that has been disconnected
*/
- public abstract void onDisconnected(CameraDevice camera); // Must implement
+ public abstract void onDisconnected(@NonNull CameraDevice camera); // Must implement
/**
* The method called when a camera device has encountered a serious error.
@@ -805,12 +835,14 @@
* @param error The error code, one of the
* {@code StateCallback.ERROR_*} values.
*
+ * @see #ERROR_CAMERA_IN_USE
+ * @see #ERROR_MAX_CAMERAS_IN_USE
+ * @see #ERROR_CAMERA_DISABLED
* @see #ERROR_CAMERA_DEVICE
* @see #ERROR_CAMERA_SERVICE
- * @see #ERROR_CAMERA_DISABLED
- * @see #ERROR_CAMERA_IN_USE
*/
- public abstract void onError(CameraDevice camera, int error); // Must implement
+ public abstract void onError(@NonNull CameraDevice camera,
+ @ErrorCode int error); // Must implement
}
/**
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index d99cce7..20e1610 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -16,6 +16,9 @@
package android.hardware.camera2;
+import android.annotation.RequiresPermission;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceListener;
@@ -86,6 +89,7 @@
*
* @return The list of currently connected camera devices.
*/
+ @NonNull
public String[] getCameraIdList() throws CameraAccessException {
synchronized (mLock) {
// ID list creation handles various known failures in device enumeration, so only
@@ -121,7 +125,8 @@
* @throws IllegalArgumentException if the handler is {@code null} but the current thread has
* no looper.
*/
- public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
+ public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback,
+ @Nullable Handler handler) {
if (handler == null) {
Looper looper = Looper.myLooper();
if (looper == null) {
@@ -142,7 +147,7 @@
*
* @param callback The callback to remove from the notification list
*/
- public void unregisterAvailabilityCallback(AvailabilityCallback callback) {
+ public void unregisterAvailabilityCallback(@NonNull AvailabilityCallback callback) {
CameraManagerGlobal.get().unregisterAvailabilityCallback(callback);
}
@@ -168,7 +173,7 @@
* @throws IllegalArgumentException if the handler is {@code null} but the current thread has
* no looper.
*/
- public void registerTorchCallback(TorchCallback callback, Handler handler) {
+ public void registerTorchCallback(@NonNull TorchCallback callback, @Nullable Handler handler) {
if (handler == null) {
Looper looper = Looper.myLooper();
if (looper == null) {
@@ -188,7 +193,7 @@
*
* @param callback The callback to remove from the notification list
*/
- public void unregisterTorchCallback(TorchCallback callback) {
+ public void unregisterTorchCallback(@NonNull TorchCallback callback) {
CameraManagerGlobal.get().unregisterTorchCallback(callback);
}
@@ -201,15 +206,13 @@
*
* @throws IllegalArgumentException if the cameraId does not match any
* known camera device.
- * @throws CameraAccessException if the camera is disabled by device policy, or
- * the camera device has been disconnected.
- * @throws SecurityException if the application does not have permission to
- * access the camera
+ * @throws CameraAccessException if the camera device has been disconnected.
*
* @see #getCameraIdList
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*/
- public CameraCharacteristics getCameraCharacteristics(String cameraId)
+ @NonNull
+ public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId)
throws CameraAccessException {
CameraCharacteristics characteristics = null;
@@ -431,8 +434,9 @@
* @see #getCameraIdList
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*/
- public void openCamera(String cameraId, final CameraDevice.StateCallback callback,
- Handler handler)
+ @RequiresPermission(android.Manifest.permission.CAMERA)
+ public void openCamera(@NonNull String cameraId,
+ @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
throws CameraAccessException {
if (cameraId == null) {
@@ -444,7 +448,7 @@
handler = new Handler();
} else {
throw new IllegalArgumentException(
- "Looper doesn't exist in the calling thread");
+ "Handler argument is null, but no looper exists in the calling thread");
}
}
@@ -490,7 +494,8 @@
* or previously available camera device, or the camera device doesn't have a
* flash unit.
*/
- public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException {
+ public void setTorchMode(@NonNull String cameraId, boolean enabled)
+ throws CameraAccessException {
CameraManagerGlobal.get().setTorchMode(cameraId, enabled);
}
@@ -517,7 +522,7 @@
*
* @param cameraId The unique identifier of the new camera.
*/
- public void onCameraAvailable(String cameraId) {
+ public void onCameraAvailable(@NonNull String cameraId) {
// default empty implementation
}
@@ -532,7 +537,7 @@
*
* @param cameraId The unique identifier of the disconnected camera.
*/
- public void onCameraUnavailable(String cameraId) {
+ public void onCameraUnavailable(@NonNull String cameraId) {
// default empty implementation
}
}
@@ -572,7 +577,7 @@
* @param cameraId The unique identifier of the camera whose torch mode has become
* unavailable.
*/
- public void onTorchModeUnavailable(String cameraId) {
+ public void onTorchModeUnavailable(@NonNull String cameraId) {
// default empty implementation
}
@@ -589,7 +594,7 @@
* off. {@code false} when the torch mode has becomes off and available to
* be turned on.
*/
- public void onTorchModeChanged(String cameraId, boolean enabled) {
+ public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) {
// default empty implementation
}
}
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 6baa660..cf091a9 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -16,6 +16,7 @@
package android.hardware.camera2;
+import android.annotation.NonNull;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
@@ -103,6 +104,7 @@
* @return List of the keys contained in this map.
*/
@SuppressWarnings("unchecked")
+ @NonNull
public List<TKey> getKeys() {
Class<CameraMetadata<TKey>> thisClass = (Class<CameraMetadata<TKey>>) getClass();
return Collections.unmodifiableList(
diff --git a/core/java/android/hardware/camera2/CaptureFailure.java b/core/java/android/hardware/camera2/CaptureFailure.java
index c168ff1..8bb33f1 100644
--- a/core/java/android/hardware/camera2/CaptureFailure.java
+++ b/core/java/android/hardware/camera2/CaptureFailure.java
@@ -15,6 +15,12 @@
*/
package android.hardware.camera2;
+import android.annotation.NonNull;
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A report of failed capture for a single image capture from the image sensor.
*
@@ -43,6 +49,13 @@
*/
public static final int REASON_FLUSHED = 1;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ {REASON_ERROR,
+ REASON_FLUSHED })
+ public @interface FailureReason {};
+
private final CaptureRequest mRequest;
private final int mReason;
private final boolean mDropped;
@@ -52,8 +65,8 @@
/**
* @hide
*/
- public CaptureFailure(CaptureRequest request, int reason, boolean dropped, int sequenceId,
- long frameNumber) {
+ public CaptureFailure(CaptureRequest request, int reason,
+ boolean dropped, int sequenceId, long frameNumber) {
mRequest = request;
mReason = reason;
mDropped = dropped;
@@ -81,6 +94,7 @@
*
* @return The request associated with this failed capture. Never {@code null}.
*/
+ @NonNull
public CaptureRequest getRequest() {
return mRequest;
}
@@ -110,6 +124,7 @@
* @see #REASON_ERROR
* @see #REASON_FLUSHED
*/
+ @FailureReason
public int getReason() {
return mReason;
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 5fb9abc..8f7b983 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -16,6 +16,8 @@
package android.hardware.camera2;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
@@ -117,6 +119,7 @@
*
* @return String representation of the key name
*/
+ @NonNull
public String getName() {
return mKey.getName();
}
@@ -239,6 +242,7 @@
* @param key The result field to read.
* @return The value of that key, or {@code null} if the field is not set.
*/
+ @Nullable
public <T> T get(Key<T> key) {
return mSettings.get(key);
}
@@ -268,6 +272,7 @@
* {@inheritDoc}
*/
@Override
+ @NonNull
public List<Key<?>> getKeys() {
// Force the javadoc for this function to show up on the CaptureRequest page
return super.getKeys();
@@ -286,6 +291,7 @@
* no tag has been set.
* @see Builder#setTag
*/
+ @Nullable
public Object getTag() {
return mUserTag;
}
@@ -476,7 +482,7 @@
*
* @param outputTarget Surface to use as an output target for this request
*/
- public void addTarget(Surface outputTarget) {
+ public void addTarget(@NonNull Surface outputTarget) {
mRequest.mSurfaceSet.add(outputTarget);
}
@@ -487,7 +493,7 @@
*
* @param outputTarget Surface to use as an output target for this request
*/
- public void removeTarget(Surface outputTarget) {
+ public void removeTarget(@NonNull Surface outputTarget) {
mRequest.mSurfaceSet.remove(outputTarget);
}
@@ -499,7 +505,7 @@
* @param value The value to set the field to, which must be of a matching
* type to the key.
*/
- public <T> void set(Key<T> key, T value) {
+ public <T> void set(@NonNull Key<T> key, T value) {
mRequest.mSettings.set(key, value);
}
@@ -512,6 +518,7 @@
* @param key The metadata field to read.
* @return The value of that key, or {@code null} if the field is not set.
*/
+ @Nullable
public <T> T get(Key<T> key) {
return mRequest.mSettings.get(key);
}
@@ -527,7 +534,7 @@
* @param tag an arbitrary Object to store with this request
* @see CaptureRequest#getTag
*/
- public void setTag(Object tag) {
+ public void setTag(@Nullable Object tag) {
mRequest.mUserTag = tag;
}
@@ -543,6 +550,7 @@
* @return A new capture request instance, ready for submission to the
* camera device.
*/
+ @NonNull
public CaptureRequest build() {
return new CaptureRequest(mRequest);
}
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 0277c5b..f4017d0 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -16,6 +16,8 @@
package android.hardware.camera2;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.CaptureResultExtras;
import android.hardware.camera2.impl.PublicKey;
@@ -102,6 +104,7 @@
*
* @return String representation of the key name
*/
+ @NonNull
public String getName() {
return mKey.getName();
}
@@ -216,6 +219,7 @@
* @param key The result field to read.
* @return The value of that key, or {@code null} if the field is not set.
*/
+ @Nullable
public <T> T get(Key<T> key) {
T value = mResults.get(key);
if (VERBOSE) Log.v(TAG, "#get for Key = " + key.getName() + ", returned value = " + value);
@@ -259,6 +263,7 @@
* {@inheritDoc}
*/
@Override
+ @NonNull
public List<Key<?>> getKeys() {
// Force the javadoc for this function to show up on the CaptureResult page
return super.getKeys();
@@ -285,6 +290,7 @@
*
* @return The request associated with this result. Never {@code null}.
*/
+ @NonNull
public CaptureRequest getRequest() {
return mRequest;
}
diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java
index f16d650..70afe5b 100644
--- a/core/java/android/hardware/camera2/DngCreator.java
+++ b/core/java/android/hardware/camera2/DngCreator.java
@@ -16,6 +16,9 @@
package android.hardware.camera2;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.ImageFormat;
@@ -80,7 +83,8 @@
* {@link android.hardware.camera2.CameraCharacteristics}.
* @param metadata a metadata object to generate tags from.
*/
- public DngCreator(CameraCharacteristics characteristics, CaptureResult metadata) {
+ public DngCreator(@NonNull CameraCharacteristics characteristics,
+ @NonNull CaptureResult metadata) {
if (characteristics == null || metadata == null) {
throw new IllegalArgumentException("Null argument to DngCreator constructor");
}
@@ -126,6 +130,7 @@
* </ul>
* @return this {@link #DngCreator} object.
*/
+ @NonNull
public DngCreator setOrientation(int orientation) {
if (orientation < ExifInterface.ORIENTATION_UNDEFINED ||
orientation > ExifInterface.ORIENTATION_ROTATE_270) {
@@ -150,7 +155,8 @@
* @throws java.lang.IllegalArgumentException if the given thumbnail image has a dimension
* larger than {@link #MAX_THUMBNAIL_DIMENSION}.
*/
- public DngCreator setThumbnail(Bitmap pixels) {
+ @NonNull
+ public DngCreator setThumbnail(@NonNull Bitmap pixels) {
if (pixels == null) {
throw new IllegalArgumentException("Null argument to setThumbnail");
}
@@ -185,7 +191,8 @@
* @throws java.lang.IllegalArgumentException if the given thumbnail image has a dimension
* larger than {@link #MAX_THUMBNAIL_DIMENSION}.
*/
- public DngCreator setThumbnail(Image pixels) {
+ @NonNull
+ public DngCreator setThumbnail(@NonNull Image pixels) {
if (pixels == null) {
throw new IllegalArgumentException("Null argument to setThumbnail");
}
@@ -226,7 +233,8 @@
* @throws java.lang.IllegalArgumentException if the given location object doesn't
* contain enough information to set location metadata.
*/
- public DngCreator setLocation(Location location) {
+ @NonNull
+ public DngCreator setLocation(@NonNull Location location) {
if (location == null) {
throw new IllegalArgumentException("Null location passed to setLocation");
}
@@ -258,7 +266,8 @@
* @param description the user description string.
* @return this {@link #DngCreator} object.
*/
- public DngCreator setDescription(String description) {
+ @NonNull
+ public DngCreator setDescription(@NonNull String description) {
if (description == null) {
throw new IllegalArgumentException("Null description passed to setDescription.");
}
@@ -293,8 +302,8 @@
* set to write a well-formatted DNG file.
* @throws java.lang.IllegalArgumentException if the size passed in does not match the
*/
- public void writeInputStream(OutputStream dngOutput, Size size, InputStream pixels, long offset)
- throws IOException {
+ public void writeInputStream(@NonNull OutputStream dngOutput, @NonNull Size size,
+ @NonNull InputStream pixels, @IntRange(from=0) long offset) throws IOException {
if (dngOutput == null) {
throw new IllegalArgumentException("Null dngOutput passed to writeInputStream");
} else if (size == null) {
@@ -345,7 +354,8 @@
* @throws java.lang.IllegalStateException if not enough metadata information has been
* set to write a well-formatted DNG file.
*/
- public void writeByteBuffer(OutputStream dngOutput, Size size, ByteBuffer pixels, long offset)
+ public void writeByteBuffer(@NonNull OutputStream dngOutput, @NonNull Size size,
+ @NonNull ByteBuffer pixels, @IntRange(from=0) long offset)
throws IOException {
if (dngOutput == null) {
throw new IllegalArgumentException("Null dngOutput passed to writeByteBuffer");
@@ -381,7 +391,8 @@
* @throws java.lang.IllegalStateException if not enough metadata information has been
* set to write a well-formatted DNG file.
*/
- public void writeImage(OutputStream dngOutput, Image pixels) throws IOException {
+ public void writeImage(@NonNull OutputStream dngOutput, @NonNull Image pixels)
+ throws IOException {
if (dngOutput == null) {
throw new IllegalArgumentException("Null dngOutput to writeImage");
} else if (pixels == null) {
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index fb3c098..657745c 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -16,6 +16,7 @@
package android.hardware.camera2;
+import android.annotation.NonNull;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.CaptureResultExtras;
@@ -96,6 +97,7 @@
*
* @return unmodifiable list of partial results
*/
+ @NonNull
public List<CaptureResult> getPartialResults() {
return Collections.unmodifiableList(mPartialResults);
}
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/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 4508dc8..c4e8b15 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -607,6 +607,8 @@
}
public void prepare(Surface surface) throws CameraAccessException {
+ if (surface == null) throw new IllegalArgumentException("Surface is null");
+
synchronized(mInterfaceLock) {
int streamId = -1;
for (int i = 0; i < mConfiguredOutputs.size(); i++) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 26878c0..e175e9a 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1906,9 +1906,6 @@
*
* @return {@link ProxyInfo} for the current global HTTP proxy or {@code null}
* if no global HTTP proxy is set.
- *
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* @hide
*/
public ProxyInfo getGlobalProxy() {
@@ -1920,6 +1917,28 @@
}
/**
+ * Retrieve the global HTTP proxy, or if no global HTTP proxy is set, a
+ * network-specific HTTP proxy. If {@code network} is null, the
+ * network-specific proxy returned is the proxy of the default active
+ * network.
+ *
+ * @return {@link ProxyInfo} for the current global HTTP proxy, or if no
+ * global HTTP proxy is set, {@code ProxyInfo} for {@code network},
+ * or when {@code network} is {@code null},
+ * the {@code ProxyInfo} for the default active network. Returns
+ * {@code null} when no proxy applies or the caller doesn't have
+ * permission to use {@code network}.
+ * @hide
+ */
+ public ProxyInfo getProxyForNetwork(Network network) {
+ try {
+ return mService.getProxyForNetwork(network);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
* Get the current default HTTP proxy settings. If a global proxy is set it will be returned,
* otherwise if this process is bound to a {@link Network} using
* {@link #bindProcessToNetwork} then that {@code Network}'s proxy is returned, otherwise
@@ -1929,19 +1948,7 @@
* HTTP proxy is active.
*/
public ProxyInfo getDefaultProxy() {
- final Network network = getBoundNetworkForProcess();
- if (network != null) {
- final ProxyInfo globalProxy = getGlobalProxy();
- if (globalProxy != null) return globalProxy;
- final LinkProperties lp = getLinkProperties(network);
- if (lp != null) return lp.getHttpProxy();
- return null;
- }
- try {
- return mService.getDefaultProxy();
- } catch (RemoteException e) {
- return null;
- }
+ return getProxyForNetwork(getBoundNetworkForProcess());
}
/**
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 77200a5..89d23a2 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -104,15 +104,15 @@
void setGlobalProxy(in ProxyInfo p);
- ProxyInfo getDefaultProxy();
+ ProxyInfo getProxyForNetwork(in Network nework);
- boolean prepareVpn(String oldPackage, String newPackage);
+ boolean prepareVpn(String oldPackage, String newPackage, int userId);
- void setVpnPackageAuthorization(boolean authorized);
+ void setVpnPackageAuthorization(String packageName, int userId, boolean authorized);
ParcelFileDescriptor establishVpn(in VpnConfig config);
- VpnConfig getVpnConfig();
+ VpnConfig getVpnConfig(int userId);
void startLegacyVpn(in VpnProfile profile);
diff --git a/core/java/android/net/IpReachabilityMonitor.java b/core/java/android/net/IpReachabilityMonitor.java
index 7e1c05b..add8774 100644
--- a/core/java/android/net/IpReachabilityMonitor.java
+++ b/core/java/android/net/IpReachabilityMonitor.java
@@ -75,7 +75,6 @@
private boolean mRunning;
final private Thread mObserverThread;
- // TODO: consider passing in a NetworkInterface object from the caller.
public IpReachabilityMonitor(String ifName, Callback callback) throws IllegalArgumentException {
mInterfaceName = ifName;
int ifIndex = -1;
@@ -154,6 +153,12 @@
}
}
+ private boolean stillRunning() {
+ synchronized (mLock) {
+ return mRunning;
+ }
+ }
+
public void updateLinkProperties(LinkProperties lp) {
if (!mInterfaceName.equals(lp.getInterfaceName())) {
// TODO: figure out how to cope with interface changes.
@@ -204,6 +209,47 @@
}
}
+ public void probeAll() {
+ Set<InetAddress> ipProbeList = new HashSet<InetAddress>();
+ synchronized (mLock) {
+ 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());
+ }
+ } catch (ErrnoException | InterruptedIOException | SocketException e) {
+ Log.d(TAG, "Error probing ip=" + ip.getHostAddress(), e);
+ }
+
+ if (nlSocket != null) {
+ nlSocket.close();
+ }
+ }
+
private final class NetlinkSocketObserver implements Runnable {
private static final String TAG = "NetlinkSocketObserver";
@@ -242,12 +288,6 @@
if (VDBG) { Log.d(TAG, "Finishing observing thread."); }
}
- private boolean stillRunning() {
- synchronized (mLock) {
- return mRunning;
- }
- }
-
private void clearNetlinkSocket() {
if (mSocket != null) {
mSocket.close();
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 67ecb5d..754c6b3 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -247,12 +247,7 @@
throw new IOException("No ConnectivityManager yet constructed, please construct one");
}
// TODO: Should this be optimized to avoid fetching the global proxy for every request?
- ProxyInfo proxyInfo = cm.getGlobalProxy();
- if (proxyInfo == null) {
- // TODO: Should this be optimized to avoid fetching LinkProperties for every request?
- final LinkProperties lp = cm.getLinkProperties(this);
- if (lp != null) proxyInfo = lp.getHttpProxy();
- }
+ final ProxyInfo proxyInfo = cm.getProxyForNetwork(this);
java.net.Proxy proxy = null;
if (proxyInfo != null) {
proxy = proxyInfo.makeProxy();
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index ab70485..cf747cf 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -619,6 +619,7 @@
case NET_CAPABILITY_NOT_RESTRICTED: capabilities += "NOT_RESTRICTED"; break;
case NET_CAPABILITY_TRUSTED: capabilities += "TRUSTED"; break;
case NET_CAPABILITY_NOT_VPN: capabilities += "NOT_VPN"; break;
+ case NET_CAPABILITY_VALIDATED: capabilities += "VALIDATED"; break;
}
if (++i < types.length) capabilities += "&";
}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 29dd8ad..4487cab 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -194,6 +194,12 @@
public native static boolean protectFromVpn(int socketfd);
/**
+ * Determine if {@code uid} can access network designated by {@code netId}.
+ * @return {@code true} if {@code uid} can access network, {@code false} otherwise.
+ */
+ public native static boolean queryUserAccess(int uid, int netId);
+
+ /**
* Convert a IPv4 address from an integer to an InetAddress.
* @param hostAddress an int corresponding to the IPv4 address in network byte order
*/
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index a0e65eb..2bb48b3 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -156,7 +156,7 @@
*/
public static Intent prepare(Context context) {
try {
- if (getService().prepareVpn(context.getPackageName(), null)) {
+ if (getService().prepareVpn(context.getPackageName(), null, UserHandle.myUserId())) {
return null;
}
} catch (RemoteException e) {
@@ -182,10 +182,11 @@
String packageName = context.getPackageName();
try {
// Only prepare if we're not already prepared.
- if (!cm.prepareVpn(packageName, null)) {
- cm.prepareVpn(null, packageName);
+ int userId = UserHandle.myUserId();
+ if (!cm.prepareVpn(packageName, null, userId)) {
+ cm.prepareVpn(null, packageName, userId);
}
- cm.setVpnPackageAuthorization(true);
+ cm.setVpnPackageAuthorization(packageName, userId, true);
} catch (RemoteException e) {
// ignore
}
diff --git a/core/java/android/net/netlink/RtNetlinkNeighborMessage.java b/core/java/android/net/netlink/RtNetlinkNeighborMessage.java
index d4b572c..b5f5817 100644
--- a/core/java/android/net/netlink/RtNetlinkNeighborMessage.java
+++ b/core/java/android/net/netlink/RtNetlinkNeighborMessage.java
@@ -21,9 +21,11 @@
import android.net.netlink.StructNlAttr;
import android.net.netlink.StructNlMsgHdr;
import android.net.netlink.NetlinkMessage;
+import android.system.OsConstants;
import android.util.Log;
import java.net.InetAddress;
+import java.net.Inet6Address;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -131,6 +133,34 @@
return bytes;
}
+ /**
+ * A convenience method to create an RTM_NEWNEIGH message, to modify
+ * the kernel's state information for a specific neighbor.
+ */
+ public static byte[] newNewNeighborMessage(
+ 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_seq = seqNo;
+
+ final RtNetlinkNeighborMessage msg = new RtNetlinkNeighborMessage(nlmsghdr);
+ msg.mNdmsg = new StructNdMsg();
+ msg.mNdmsg.ndm_family =
+ (byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET);
+ msg.mNdmsg.ndm_ifindex = ifIndex;
+ msg.mNdmsg.ndm_state = nudState;
+ msg.mDestination = ip;
+ msg.mLinkLayerAddr = llAddr; // might be null
+
+ final byte[] bytes = new byte[msg.getRequiredSpace()];
+ nlmsghdr.nlmsg_len = bytes.length;
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+ byteBuffer.order(ByteOrder.nativeOrder());
+ msg.pack(byteBuffer);
+ return bytes;
+ }
+
private StructNdMsg mNdmsg;
private InetAddress mDestination;
private byte[] mLinkLayerAddr;
@@ -166,6 +196,41 @@
return mCacheInfo;
}
+ public int getRequiredSpace() {
+ int spaceRequired = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE;
+ if (mDestination != null) {
+ spaceRequired += NetlinkConstants.alignedLengthOf(
+ StructNlAttr.NLA_HEADERLEN + mDestination.getAddress().length);
+ }
+ if (mLinkLayerAddr != null) {
+ spaceRequired += NetlinkConstants.alignedLengthOf(
+ StructNlAttr.NLA_HEADERLEN + mLinkLayerAddr.length);
+ }
+ // Currently we don't write messages with NDA_PROBES nor NDA_CACHEINFO
+ // attributes appended. Fix later, if necessary.
+ return spaceRequired;
+ }
+
+ private static void packNlAttr(short nlType, byte[] nlValue, ByteBuffer byteBuffer) {
+ final StructNlAttr nlAttr = new StructNlAttr();
+ nlAttr.nla_type = nlType;
+ nlAttr.nla_value = nlValue;
+ nlAttr.nla_len = (short) (StructNlAttr.NLA_HEADERLEN + nlAttr.nla_value.length);
+ nlAttr.pack(byteBuffer);
+ }
+
+ public void pack(ByteBuffer byteBuffer) {
+ getHeader().pack(byteBuffer) ;
+ mNdmsg.pack(byteBuffer);
+
+ if (mDestination != null) {
+ packNlAttr(NDA_DST, mDestination.getAddress(), byteBuffer);
+ }
+ if (mLinkLayerAddr != null) {
+ packNlAttr(NDA_LLADDR, mLinkLayerAddr, byteBuffer);
+ }
+ }
+
@Override
public String toString() {
final String ipLiteral = (mDestination == null) ? "" : mDestination.getHostAddress();
diff --git a/core/java/android/net/netlink/StructNdMsg.java b/core/java/android/net/netlink/StructNdMsg.java
index e66d45d..ca1cbbb 100644
--- a/core/java/android/net/netlink/StructNdMsg.java
+++ b/core/java/android/net/netlink/StructNdMsg.java
@@ -123,9 +123,7 @@
ndm_family = (byte) OsConstants.AF_UNSPEC;
}
- public boolean pack(ByteBuffer byteBuffer) {
- if (!hasAvailableSpace(byteBuffer)) { return false; }
-
+ public void pack(ByteBuffer byteBuffer) {
// The ByteOrder must have already been set by the caller. In most
// cases ByteOrder.nativeOrder() is correct, with the exception
// of usage within unittests.
@@ -136,7 +134,6 @@
byteBuffer.putShort(ndm_state);
byteBuffer.put(ndm_flags);
byteBuffer.put(ndm_type);
- return true;
}
public boolean nudConnected() {
diff --git a/core/java/android/net/netlink/StructNlAttr.java b/core/java/android/net/netlink/StructNlAttr.java
index 9aef4c7..597a6aa 100644
--- a/core/java/android/net/netlink/StructNlAttr.java
+++ b/core/java/android/net/netlink/StructNlAttr.java
@@ -116,6 +116,14 @@
}
}
+ public void pack(ByteBuffer byteBuffer) {
+ final int originalPosition = byteBuffer.position();
+ byteBuffer.putShort(nla_len);
+ byteBuffer.putShort(nla_type);
+ byteBuffer.put(nla_value);
+ byteBuffer.position(originalPosition + getAlignedLength());
+ }
+
@Override
public String toString() {
return "StructNlAttr{ "
diff --git a/core/java/android/net/netlink/StructNlMsgErr.java b/core/java/android/net/netlink/StructNlMsgErr.java
index 5da19a2..6b02650 100644
--- a/core/java/android/net/netlink/StructNlMsgErr.java
+++ b/core/java/android/net/netlink/StructNlMsgErr.java
@@ -57,9 +57,7 @@
msg = null;
}
- public boolean pack(ByteBuffer byteBuffer) {
- if (!hasAvailableSpace(byteBuffer)) { return false; }
-
+ 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
// exception of usage within unittests.
@@ -67,7 +65,6 @@
if (msg != null) {
msg.pack(byteBuffer);
}
- return true;
}
@Override
diff --git a/core/java/android/net/netlink/StructNlMsgHdr.java b/core/java/android/net/netlink/StructNlMsgHdr.java
index 67925ac..98ab5e7 100644
--- a/core/java/android/net/netlink/StructNlMsgHdr.java
+++ b/core/java/android/net/netlink/StructNlMsgHdr.java
@@ -39,6 +39,12 @@
public static final short NLM_F_ROOT = 0x0100;
public static final short NLM_F_MATCH = 0x0200;
public static final short NLM_F_DUMP = NLM_F_ROOT|NLM_F_MATCH;
+ // Flags for a NEW request.
+ public static final short NLM_F_REPLACE = 0x100;
+ public static final short NLM_F_EXCL = 0x200;
+ public static final short NLM_F_CREATE = 0x400;
+ public static final short NLM_F_APPEND = 0x800;
+
public static String stringForNlMsgFlags(short flags) {
final StringBuilder sb = new StringBuilder();
@@ -106,9 +112,7 @@
nlmsg_pid = 0;
}
- public boolean pack(ByteBuffer byteBuffer) {
- if (!hasAvailableSpace(byteBuffer)) { return false; }
-
+ 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
// exception of usage within unittests.
@@ -117,7 +121,6 @@
byteBuffer.putShort(nlmsg_flags);
byteBuffer.putInt(nlmsg_seq);
byteBuffer.putInt(nlmsg_pid);
- return true;
}
@Override
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index f212daf..aa4b051 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -36,6 +36,7 @@
ParcelFileDescriptor getUserIcon(int userHandle);
List<UserInfo> getUsers(boolean excludeDying);
List<UserInfo> getProfiles(int userHandle, boolean enabledOnly);
+ boolean canAddMoreManagedProfiles();
UserInfo getProfileParent(int userHandle);
UserInfo getUserInfo(int userHandle);
long getUserCreationTime(int userHandle);
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 9a0d0d0..e523285 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -136,4 +136,8 @@
public abstract void setDeviceIdleMode(boolean enabled);
public abstract void setDeviceIdleWhitelist(int[] appids);
+
+ public abstract void updateUidProcState(int uid, int procState);
+
+ public abstract void uidGone(int uid);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 3dee68c..11043b3 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -933,6 +933,22 @@
}
/**
+ * Checks whether it's possible to add more managed profiles. Caller must hold the MANAGE_USERS
+ * permission.
+ *
+ * @return true if more managed profiles can be added, false if limit has been reached.
+ * @hide
+ */
+ public boolean canAddMoreManagedProfiles() {
+ try {
+ return mService.canAddMoreManagedProfiles();
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not check if we can add more managed profiles", re);
+ return false;
+ }
+ }
+
+ /**
* Returns list of the profiles of userHandle including
* userHandle itself.
* Note that this returns both enabled and not enabled profiles. See
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/Settings.java b/core/java/android/provider/Settings.java
index ef0dc3e..37645b5 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5417,6 +5417,14 @@
public static final String EMERGENCY_ASSISTANCE_APPLICATION = "emergency_assistance_application";
/**
+ * Specifies whether the current app context on scren (assist data) will be sent to the
+ * assist application (active voice interaction service).
+ *
+ * @hide
+ */
+ public static final String ASSIST_STRUCTURE_ENABLED = "assist_structure_enabled";
+
+ /**
* Names of the packages that the current user has explicitly allowed to
* see all of the user's notifications, separated by ':'.
*
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 8c89ddb..77e2125 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -77,6 +77,7 @@
public static final int START_WITH_ASSIST = 1<<0;
/**
+ * @hide
* Flag for use with {@link #showSession}: request that the session be started with
* a screen shot of the currently focused activity.
*/
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 71b7f76..f122d10 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -820,6 +820,7 @@
public void onHandleAssist(Bundle assistBundle) {
}
+ /** @hide */
public void onHandleScreenshot(Bitmap screenshot) {
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 016541f..b93a4a5 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -168,8 +168,6 @@
final Rect mFinalStableInsets = new Rect();
final Configuration mConfiguration = new Configuration();
- private boolean mWindowIsRound;
-
final WindowManager.LayoutParams mLayout
= new WindowManager.LayoutParams();
IWindowSession mSession;
@@ -640,7 +638,6 @@
// Retrieve watch round info
TypedArray windowStyle = obtainStyledAttributes(
com.android.internal.R.styleable.Window);
- mWindowIsRound = ScreenShapeHelper.getWindowIsRound(getResources());
windowStyle.recycle();
// Add window
@@ -776,7 +773,8 @@
mFinalStableInsets.set(mDispatchedStableInsets);
mFinalSystemInsets.bottom = mIWallpaperEngine.mDisplayPadding.bottom;
WindowInsets insets = new WindowInsets(mFinalSystemInsets,
- null, mFinalStableInsets, mWindowIsRound);
+ null, mFinalStableInsets,
+ getResources().getConfiguration().isScreenRound());
onApplyWindowInsets(insets);
}
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index 92e874f..dc965ed 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -134,6 +134,24 @@
}
/**
+ * @hide
+ * Removes the mapping from the specified key, if there was any, returning the old value.
+ */
+ public E removeReturnOld(int key) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i >= 0) {
+ if (mValues[i] != DELETED) {
+ final E old = (E) mValues[i];
+ mValues[i] = DELETED;
+ mGarbage = true;
+ return old;
+ }
+ }
+ return null;
+ }
+
+ /**
* Alias for {@link #delete(int)}.
*/
public void remove(int key) {
diff --git a/core/java/android/view/ActionMode.java b/core/java/android/view/ActionMode.java
index 9f202a9..9f00455 100644
--- a/core/java/android/view/ActionMode.java
+++ b/core/java/android/view/ActionMode.java
@@ -44,6 +44,12 @@
*/
public static final int TYPE_FLOATING = 1;
+ /**
+ * Default snooze time.
+ */
+ public static final int SNOOZE_TIME_DEFAULT =
+ ViewConfiguration.getDefaultActionModeSnoozeTime();
+
private Object mTag;
private boolean mTitleOptionalHint;
private int mType = TYPE_PRIMARY;
@@ -207,6 +213,19 @@
public void invalidateContentRect() {}
/**
+ * Hide the action mode view from obstructing the content below for a short period.
+ * This only makes sense for action modes that support dynamic positioning on the screen.
+ * If this method is called again before the snooze time expires, the later snooze will
+ * cancel the former and then take effect.
+ * NOTE that there is an internal limit to how long the mode can be snoozed for. It's typically
+ * about a few seconds.
+ *
+ * @param snoozeTime The number of milliseconds to snooze for.
+ * @see #SNOOZE_TIME_DEFAULT
+ */
+ public void snooze(int snoozeTime) {}
+
+ /**
* Finish and close this action mode. The action mode's {@link ActionMode.Callback} will
* have its {@link Callback#onDestroyActionMode(ActionMode)} method called.
*/
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 4e91ad4..8c6fa3f 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -213,6 +213,11 @@
private static final int OVERFLING_DISTANCE = 6;
/**
+ * Default time to snooze an action mode for.
+ */
+ private static final int ACTION_MODE_SNOOZE_TIME_DEFAULT = 2000;
+
+ /**
* Configuration values for overriding {@link #hasPermanentMenuKey()} behavior.
* These constants must match the definition in res/values/config.xml.
*/
@@ -732,6 +737,13 @@
}
/**
+ * @return the default duration in milliseconds for {@link ActionMode#snooze(int)}.
+ */
+ public static int getDefaultActionModeSnoozeTime() {
+ return ACTION_MODE_SNOOZE_TIME_DEFAULT;
+ }
+
+ /**
* Report if the device has a permanent menu key available to the user.
*
* <p>As of Android 3.0, devices may not have a permanent menu key available.
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 b7d902c..c4f9209 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -122,7 +122,6 @@
private static final String PROPERTY_PROFILE_RENDERING = "viewroot.profile_rendering";
// properties used by emulator to determine display shape
- public static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
public static final String PROPERTY_EMULATOR_WIN_OUTSET_BOTTOM_PX =
"ro.emu.win_outset_bottom_px";
@@ -341,8 +340,6 @@
/** Set to true once doDie() has been called. */
private boolean mRemoved;
- private final boolean mWindowIsRound;
-
/**
* Consistency verifier for debugging purposes.
*/
@@ -397,7 +394,6 @@
mChoreographer = Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
loadSystemProperties();
- mWindowIsRound = ScreenShapeHelper.getWindowIsRound(context.getResources());
}
public static void addFirstDrawHandler(Runnable callback) {
@@ -1271,7 +1267,8 @@
stableInsets = mPendingStableInsets;
}
mLastWindowInsets = new WindowInsets(contentInsets,
- null /* windowDecorInsets */, stableInsets, mWindowIsRound);
+ null /* windowDecorInsets */, stableInsets,
+ mContext.getResources().getConfiguration().isScreenRound());
}
return mLastWindowInsets;
}
@@ -2024,8 +2021,8 @@
mLastWasImTarget = imTarget;
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null && imTarget) {
- imm.startGettingWindowFocus(mView);
- imm.onWindowFocus(mView, mView.findFocus(),
+ imm.onPreWindowFocus(mView, true /* hasWindowFocus */);
+ imm.onPostWindowFocus(mView, mView.findFocus(),
mWindowAttributes.softInputMode,
!mHasHadWindowFocus, mWindowAttributes.flags);
}
@@ -3330,11 +3327,10 @@
.mayUseInputMethod(mWindowAttributes.flags);
InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
+ imm.onPreWindowFocus(mView, hasWindowFocus);
+ }
if (mView != null) {
- if (hasWindowFocus && imm != null && mLastWasImTarget &&
- !isInLocalFocusMode()) {
- imm.startGettingWindowFocus(mView);
- }
mAttachInfo.mKeyDispatchState.reset();
mView.dispatchWindowFocusChanged(hasWindowFocus);
mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
@@ -3344,7 +3340,7 @@
// so all of the view state is set up correctly.
if (hasWindowFocus) {
if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
- imm.onWindowFocus(mView, mView.findFocus(),
+ imm.onPostWindowFocus(mView, mView.findFocus(),
mWindowAttributes.softInputMode,
!mHasHadWindowFocus, mWindowAttributes.flags);
}
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 8dc49acd..5c8b023 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -25,60 +25,215 @@
* View.onProvideStructure}.
*/
public abstract class ViewStructure {
+ /**
+ * Set the identifier for this view.
+ *
+ * @param id The view's identifier, as per {@link View#getId View.getId()}.
+ * @param packageName The package name of the view's identifier, or null if there is none.
+ * @param typeName The type name of the view's identifier, or null if there is none.
+ * @param entryName The entry name of the view's identifier, or null if there is none.
+ */
public abstract void setId(int id, String packageName, String typeName, String entryName);
+ /**
+ * Set the basic dimensions of this view.
+ *
+ * @param left The view's left position, in pixels relative to its parent's left edge.
+ * @param top The view's top position, in pixels relative to its parent's top edge.
+ * @param scrollX How much the view's x coordinate space has been scrolled, in pixels.
+ * @param scrollY How much the view's y coordinate space has been scrolled, in pixels.
+ * @param width The view's visible width, in pixels. This is the width visible on screen,
+ * not the total data width of a scrollable view.
+ * @param height The view's visible height, in pixels. This is the height visible on
+ * screen, not the total data height of a scrollable view.
+ */
public abstract void setDimens(int left, int top, int scrollX, int scrollY, int width,
int height);
+ /**
+ * Set the visibility state of this view, as per
+ * {@link View#getVisibility View.getVisibility()}.
+ */
public abstract void setVisibility(int visibility);
/** @hide */
public abstract void setAssistBlocked(boolean state);
+ /**
+ * Set the enabled state of this view, as per {@link View#isEnabled View.isEnabled()}.
+ */
public abstract void setEnabled(boolean state);
+ /**
+ * Set the clickable state of this view, as per {@link View#isClickable View.isClickable()}.
+ */
public abstract void setClickable(boolean state);
+ /**
+ * Set the long clickable state of this view, as per
+ * {@link View#isLongClickable View.isLongClickable()}.
+ */
public abstract void setLongClickable(boolean state);
+ /**
+ * Set the stylus button pressable state of this view, as per
+ * {@link View#isStylusButtonPressable View.isStylusButtonPressable()}.
+ */
public abstract void setStylusButtonPressable(boolean state);
+ /**
+ * Set the focusable state of this view, as per {@link View#isFocusable View.isFocusable()}.
+ */
public abstract void setFocusable(boolean state);
+ /**
+ * Set the focused state of this view, as per {@link View#isFocused View.isFocused()}.
+ */
public abstract void setFocused(boolean state);
+ /**
+ * Set the accessibility focused state of this view, as per
+ * {@link View#isAccessibilityFocused View.isAccessibilityFocused()}.
+ */
public abstract void setAccessibilityFocused(boolean state);
+ /**
+ * Set the checkable state of this view, such as whether it implements the
+ * {@link android.widget.Checkable} interface.
+ */
public abstract void setCheckable(boolean state);
+ /**
+ * Set the checked state of this view, such as
+ * {@link android.widget.Checkable#isChecked Checkable.isChecked()}.
+ */
public abstract void setChecked(boolean state);
+ /**
+ * Set the selected state of this view, as per {@link View#isSelected View.isSelected()}.
+ */
public abstract void setSelected(boolean state);
+ /**
+ * Set the activated state of this view, as per {@link View#isActivated View.isActivated()}.
+ */
public abstract void setActivated(boolean state);
+ /**
+ * Set the class name of the view, as per
+ * {@link View#getAccessibilityClassName View.getAccessibilityClassName()}.
+ */
public abstract void setClassName(String className);
+ /**
+ * Set the content description of the view, as per
+ * {@link View#getContentDescription View.getContentDescription()}.
+ */
public abstract void setContentDescription(CharSequence contentDescription);
+ /**
+ * Set the text that is associated with this view. There is no selection
+ * associated with the text. The text may have style spans to supply additional
+ * display and semantic information.
+ */
public abstract void setText(CharSequence text);
+
+ /**
+ * Like {@link #setText(CharSequence)} but with an active selection
+ * extending from <var>selectionStart</var> through <var>selectionEnd</var>.
+ */
public abstract void setText(CharSequence text, int selectionStart, int selectionEnd);
+
+ /**
+ * Set default global style of the text previously set with
+ * {@link #setText}, derived from the given TextPaint object. Size, foreground color,
+ * background color, and style information will be extracted from the paint.
+ */
public abstract void setTextPaint(TextPaint paint);
+
+ /**
+ * Explicitly set default global style information for text that was previously set with
+ * {@link #setText}.
+ *
+ * @param size The size, in pixels, of the text.
+ * @param fgColor The foreground color, packed as 0xAARRGGBB.
+ * @param bgColor The background color, packed as 0xAARRGGBB.
+ * @param style Style flags, as defined by {@link android.app.AssistStructure.ViewNode}.
+ */
+ public abstract void setTextStyle(int size, int fgColor, int bgColor, int style);
+
+ /**
+ * Set optional hint text associated with this view; this is for example the text that is
+ * shown by an EditText when it is empty to indicate to the user the kind of text to input.
+ */
public abstract void setHint(CharSequence hint);
+ /**
+ * Retrieve the last {@link #setText(CharSequence)}.
+ */
public abstract CharSequence getText();
+
+ /**
+ * Retrieve the last selection start set by {@link #setText(CharSequence, int, int)}.
+ */
public abstract int getTextSelectionStart();
+
+ /**
+ * Retrieve the last selection end set by {@link #setText(CharSequence, int, int)}.
+ */
public abstract int getTextSelectionEnd();
+
+ /**
+ * Retrieve the last hint set by {@link #setHint}.
+ */
public abstract CharSequence getHint();
+ /**
+ * Get extra data associated with this view structure; the returned Bundle is mutable,
+ * allowing you to view and modify its contents. Keys placed in the Bundle should use
+ * an appropriate namespace prefix (such as com.google.MY_KEY) to avoid conflicts.
+ */
public abstract Bundle getExtras();
+
+ /**
+ * Returns true if {@link #getExtras} has been used to create extra content.
+ */
public abstract boolean hasExtras();
+ /**
+ * Set the number of children of this view, which defines the range of indices you can
+ * use with {@link #newChild} and {@link #asyncNewChild}. Calling this method again
+ * resets all of the child state of the view, removing any children that had previously
+ * been added.
+ */
public abstract void setChildCount(int num);
+
+ /**
+ * Return the child count as set by {@link #setChildCount}.
+ */
public abstract int getChildCount();
+
+ /**
+ * Create a new child {@link ViewStructure} in this view, putting into the list of
+ * children at <var>index</var>.
+ * @return Returns an fresh {@link ViewStructure} ready to be filled in.
+ */
public abstract ViewAssistStructure newChild(int index);
+ /**
+ * Like {@link #newChild}, but allows the caller to asynchronously populate the returned
+ * child. It can transfer the returned {@link ViewStructure} to another thread for it
+ * to build its content (and children etc). Once done, some thread must call
+ * {@link #asyncCommit} to tell the containing {@link ViewStructure} that the async
+ * population is done.
+ * @return Returns an fresh {@link ViewStructure} ready to be filled in.
+ */
public abstract ViewAssistStructure asyncNewChild(int index);
+
+ /**
+ * Call when done populating a {@link ViewStructure} returned by
+ * {@link #asyncNewChild}.
+ */
public abstract void asyncCommit();
/** @hide */
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 568e160..824b434 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1289,14 +1289,14 @@
void focusInLocked(View view) {
if (DEBUG) Log.v(TAG, "focusIn: " + view);
-
+
if (mCurRootView != view.getRootView()) {
// This is a request from a window that isn't in the window with
// IME focus, so ignore it.
if (DEBUG) Log.v(TAG, "Not IME target window, ignoring");
return;
}
-
+
mNextServedView = view;
scheduleCheckFocusLocked(view);
}
@@ -1392,7 +1392,7 @@
* Called by ViewAncestor when its window gets input focus.
* @hide
*/
- public void onWindowFocus(View rootView, View focusedView, int softInputMode,
+ public void onPostWindowFocus(View rootView, View focusedView, int softInputMode,
boolean first, int windowFlags) {
boolean forceNewFocus = false;
synchronized (mH) {
@@ -1441,14 +1441,27 @@
}
}
}
-
+
/** @hide */
- public void startGettingWindowFocus(View rootView) {
+ public void onPreWindowFocus(View rootView, boolean hasWindowFocus) {
synchronized (mH) {
- mCurRootView = rootView;
+ if (rootView == null) {
+ mCurRootView = null;
+ } if (hasWindowFocus) {
+ mCurRootView = rootView;
+ } else if (rootView == mCurRootView) {
+ // If the mCurRootView is losing window focus, release the strong reference to it
+ // so as not to prevent it from being garbage-collected.
+ mCurRootView = null;
+ } else {
+ if (DEBUG) {
+ Log.v(TAG, "Ignoring onPreWindowFocus()."
+ + " mCurRootView=" + mCurRootView + " rootView=" + rootView);
+ }
+ }
}
}
-
+
/**
* Report the current selection range.
*
diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java
index ab6a2f9..b3b95f8 100644
--- a/core/java/android/webkit/FindActionModeCallback.java
+++ b/core/java/android/webkit/FindActionModeCallback.java
@@ -154,9 +154,9 @@
}
public void showSoftInput() {
- mInput.startGettingWindowFocus(mEditText.getRootView());
- mInput.focusIn(mEditText);
- mInput.showSoftInput(mEditText, 0);
+ if (mEditText.requestFocus()) {
+ mInput.showSoftInput(mEditText, 0);
+ }
}
public void updateMatchCount(int matchIndex, int matchCount, boolean isEmptyFind) {
diff --git a/core/java/android/webkit/ViewAssistStructure.java b/core/java/android/webkit/ViewAssistStructure.java
index 6f7a645..bbaceee 100644
--- a/core/java/android/webkit/ViewAssistStructure.java
+++ b/core/java/android/webkit/ViewAssistStructure.java
@@ -137,6 +137,11 @@
}
@Override
+ public void setTextStyle(int size, int fgColor, int bgColor, int style) {
+ mV.setTextStyle(size, fgColor, bgColor, style);
+ }
+
+ @Override
public void setHint(CharSequence hint) {
mV.setHint(hint);
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index c829783..d558c7b 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -233,6 +233,24 @@
final CursorAnchorInfoNotifier mCursorAnchorInfoNotifier = new CursorAnchorInfoNotifier();
+ private final Runnable mHideFloatingToolbar = new Runnable() {
+ @Override
+ public void run() {
+ if (mSelectionActionMode != null) {
+ mSelectionActionMode.snooze(ActionMode.SNOOZE_TIME_DEFAULT);
+ }
+ }
+ };
+
+ private final Runnable mShowFloatingToolbar = new Runnable() {
+ @Override
+ public void run() {
+ if (mSelectionActionMode != null) {
+ mSelectionActionMode.snooze(0); // snooze off.
+ }
+ }
+ };
+
Editor(TextView textView) {
mTextView = textView;
// Synchronize the filter list, which places the undo input filter at the end.
@@ -358,6 +376,9 @@
mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable);
}
+ mTextView.removeCallbacks(mHideFloatingToolbar);
+ mTextView.removeCallbacks(mShowFloatingToolbar);
+
destroyDisplayListsData();
if (mSpellChecker != null) {
@@ -1169,6 +1190,8 @@
}
void onTouchEvent(MotionEvent event) {
+ updateFloatingToolbarVisibility(event);
+
if (hasSelectionController()) {
getSelectionController().onTouchEvent(event);
}
@@ -1189,6 +1212,37 @@
}
}
+ private void updateFloatingToolbarVisibility(MotionEvent event) {
+ if (mSelectionActionMode != null) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_MOVE:
+ hideFloatingToolbar();
+ break;
+ case MotionEvent.ACTION_UP: // fall through
+ case MotionEvent.ACTION_CANCEL:
+ showFloatingToolbar();
+ }
+ }
+ }
+
+ private void hideFloatingToolbar() {
+ if (mSelectionActionMode != null) {
+ mTextView.removeCallbacks(mShowFloatingToolbar);
+ // Delay the "hide" a little bit just in case a "show" will happen almost immediately.
+ mTextView.postDelayed(mHideFloatingToolbar, 100);
+ }
+ }
+
+ private void showFloatingToolbar() {
+ if (mSelectionActionMode != null) {
+ mTextView.removeCallbacks(mHideFloatingToolbar);
+ // Delay "show" so it doesn't interfere with click confirmations
+ // or double-clicks that could "dismiss" the floating toolbar.
+ int delay = ViewConfiguration.getDoubleTapTimeout();
+ mTextView.postDelayed(mShowFloatingToolbar, delay);
+ }
+ }
+
public void beginBatchEdit() {
mInBatchEditControllers = true;
final InputMethodState ims = mInputMethodState;
@@ -3661,6 +3715,8 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
+ updateFloatingToolbarVisibility(ev);
+
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
startTouchUpFilter(getCurrentCursorOffset());
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 5acd79f..a93e7ef 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -24,6 +24,7 @@
import android.annotation.StringRes;
import android.annotation.StyleRes;
import android.annotation.XmlRes;
+import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
@@ -1465,16 +1466,22 @@
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PROCESS_TEXT_REQUEST_CODE) {
- CharSequence result = data != null
- ? data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT)
- : "";
- if (isTextEditable()) {
- replaceSelectionWithText(result);
- } else {
- if (result.length() > 0) {
- Toast.makeText(getContext(), String.valueOf(result), Toast.LENGTH_LONG).show();
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ CharSequence result = data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT);
+ if (result != null) {
+ if (isTextEditable()) {
+ replaceSelectionWithText(result);
+ } else {
+ if (result.length() > 0) {
+ Toast.makeText(getContext(), String.valueOf(result), Toast.LENGTH_LONG)
+ .show();
+ }
+ }
}
}
+ if (mEditor.hasSelectionController()) {
+ mEditor.startSelectionActionModeWithSelection();
+ }
}
}
@@ -9279,7 +9286,6 @@
void replaceSelectionWithText(CharSequence text) {
((Editable) mText).replace(getSelectionStart(), getSelectionEnd(), text);
- mEditor.startSelectionActionModeWithSelection();
}
/**
diff --git a/core/java/com/android/internal/alsa/AlsaCardsParser.java b/core/java/com/android/internal/alsa/AlsaCardsParser.java
index 5c0a888..67ee085 100644
--- a/core/java/com/android/internal/alsa/AlsaCardsParser.java
+++ b/core/java/com/android/internal/alsa/AlsaCardsParser.java
@@ -29,7 +29,7 @@
*/
public class AlsaCardsParser {
private static final String TAG = "AlsaCardsParser";
- protected static final boolean DEBUG = true;
+ protected static final boolean DEBUG = false;
private static final String kCardsFilePath = "/proc/asound/cards";
diff --git a/core/java/com/android/internal/app/ProcessMap.java b/core/java/com/android/internal/app/ProcessMap.java
index 6ff0304..23a8bd7 100644
--- a/core/java/com/android/internal/app/ProcessMap.java
+++ b/core/java/com/android/internal/app/ProcessMap.java
@@ -39,14 +39,16 @@
return value;
}
- public void remove(String name, int uid) {
+ public E remove(String name, int uid) {
SparseArray<E> uids = mMap.get(name);
if (uids != null) {
- uids.remove(uid);
+ final E old = uids.removeReturnOld(uid);
if (uids.size() == 0) {
mMap.remove(name);
}
+ return old;
}
+ return null;
}
public ArrayMap<String, SparseArray<E>> getMap() {
diff --git a/core/java/com/android/internal/logging/MetricsConstants.java b/core/java/com/android/internal/logging/MetricsConstants.java
index 65dc743..b9a75d3 100644
--- a/core/java/com/android/internal/logging/MetricsConstants.java
+++ b/core/java/com/android/internal/logging/MetricsConstants.java
@@ -208,6 +208,7 @@
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/java/com/android/internal/util/ScreenShapeHelper.java b/core/java/com/android/internal/util/ScreenShapeHelper.java
index 1bcc7a0..58ae853 100644
--- a/core/java/com/android/internal/util/ScreenShapeHelper.java
+++ b/core/java/com/android/internal/util/ScreenShapeHelper.java
@@ -33,16 +33,4 @@
}
return 0;
}
-
- /**
- * Get whether a device has has a round screen.
- */
- public static boolean getWindowIsRound(Resources resources) {
- if (IS_EMULATOR) {
- return SystemProperties.getBoolean(ViewRootImpl.PROPERTY_EMULATOR_CIRCULAR, false);
- } else {
- return resources.getBoolean(
- com.android.internal.R.bool.config_windowIsRound);
- }
- }
}
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index c3f4da7..0195208 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -30,6 +30,9 @@
public class FloatingActionMode extends ActionMode {
+ private static final int MAX_SNOOZE_TIME = 3000;
+ private static final int MOVING_HIDE_DELAY = 300;
+
private final Context mContext;
private final ActionMode.Callback2 mCallback;
private final MenuBuilder mMenu;
@@ -37,13 +40,29 @@
private final Rect mContentRectOnWindow;
private final Rect mPreviousContentRectOnWindow;
private final int[] mViewPosition;
+ private final Rect mViewRect;
+ private final Rect mScreenRect;
private final View mOriginatingView;
+
+ private final Runnable mMovingOff = new Runnable() {
+ public void run() {
+ mFloatingToolbarVisibilityHelper.setMoving(false);
+ }
+ };
+
+ private final Runnable mSnoozeOff = new Runnable() {
+ public void run() {
+ mFloatingToolbarVisibilityHelper.setSnoozed(false);
+ }
+ };
+
private FloatingToolbar mFloatingToolbar;
+ private FloatingToolbarVisibilityHelper mFloatingToolbarVisibilityHelper;
public FloatingActionMode(
Context context, ActionMode.Callback2 callback, View originatingView) {
- mContext = context;
- mCallback = callback;
+ mContext = Preconditions.checkNotNull(context);
+ mCallback = Preconditions.checkNotNull(callback);
mMenu = new MenuBuilder(context).setDefaultShowAsAction(
MenuItem.SHOW_AS_ACTION_IF_ROOM);
setType(ActionMode.TYPE_FLOATING);
@@ -51,7 +70,10 @@
mContentRectOnWindow = new Rect();
mPreviousContentRectOnWindow = new Rect();
mViewPosition = new int[2];
- mOriginatingView = originatingView;
+ mViewRect = new Rect();
+ mScreenRect = new Rect();
+ mOriginatingView = Preconditions.checkNotNull(originatingView);
+ mOriginatingView.getLocationInWindow(mViewPosition);
}
public void setFloatingToolbar(FloatingToolbar floatingToolbar) {
@@ -63,6 +85,7 @@
return mCallback.onActionItemClicked(FloatingActionMode.this, item);
}
});
+ mFloatingToolbarVisibilityHelper = new FloatingToolbarVisibilityHelper(mFloatingToolbar);
}
@Override
@@ -82,7 +105,7 @@
@Override
public void invalidate() {
- Preconditions.checkNotNull(mFloatingToolbar);
+ checkToolbarInitialized();
mCallback.onPrepareActionMode(this, mMenu);
mFloatingToolbar.updateLayout();
invalidateContentRect();
@@ -90,32 +113,79 @@
@Override
public void invalidateContentRect() {
- Preconditions.checkNotNull(mFloatingToolbar);
+ checkToolbarInitialized();
mCallback.onGetContentRect(this, mOriginatingView, mContentRect);
repositionToolbar();
}
public void updateViewLocationInWindow() {
- Preconditions.checkNotNull(mFloatingToolbar);
+ checkToolbarInitialized();
mOriginatingView.getLocationInWindow(mViewPosition);
+ mViewRect.set(
+ mViewPosition[0],
+ mViewPosition[1],
+ mViewPosition[0] + mOriginatingView.getWidth(),
+ mViewPosition[1] + mOriginatingView.getHeight());
repositionToolbar();
}
private void repositionToolbar() {
+ checkToolbarInitialized();
mContentRectOnWindow.set(
mContentRect.left + mViewPosition[0],
mContentRect.top + mViewPosition[1],
mContentRect.right + mViewPosition[0],
mContentRect.bottom + mViewPosition[1]);
if (!mContentRectOnWindow.equals(mPreviousContentRectOnWindow)) {
+ if (!mPreviousContentRectOnWindow.isEmpty()) {
+ notifyContentRectMoving();
+ }
mFloatingToolbar.setContentRect(mContentRectOnWindow);
mFloatingToolbar.updateLayout();
}
mPreviousContentRectOnWindow.set(mContentRectOnWindow);
+
+ if (isContentRectWithinBounds()) {
+ mFloatingToolbarVisibilityHelper.setOutOfBounds(false);
+ } else {
+ mFloatingToolbarVisibilityHelper.setOutOfBounds(true);
+ }
+ }
+
+ private boolean isContentRectWithinBounds() {
+ mScreenRect.set(
+ 0,
+ 0,
+ mContext.getResources().getDisplayMetrics().widthPixels,
+ mContext.getResources().getDisplayMetrics().heightPixels);
+
+ return Rect.intersects(mContentRectOnWindow, mScreenRect)
+ && Rect.intersects(mContentRectOnWindow, mViewRect);
+ }
+
+ private void notifyContentRectMoving() {
+ mOriginatingView.removeCallbacks(mMovingOff);
+ mFloatingToolbarVisibilityHelper.setMoving(true);
+ mOriginatingView.postDelayed(mMovingOff, MOVING_HIDE_DELAY);
+ }
+
+ @Override
+ public void snooze(int snoozeTime) {
+ checkToolbarInitialized();
+ snoozeTime = Math.min(MAX_SNOOZE_TIME, snoozeTime);
+ mOriginatingView.removeCallbacks(mSnoozeOff);
+ if (snoozeTime <= 0) {
+ mSnoozeOff.run();
+ } else {
+ mFloatingToolbarVisibilityHelper.setSnoozed(true);
+ mOriginatingView.postDelayed(mSnoozeOff, snoozeTime);
+ }
}
@Override
public void finish() {
+ checkToolbarInitialized();
+ reset();
mCallback.onDestroyActionMode(this);
}
@@ -144,4 +214,56 @@
return new MenuInflater(mContext);
}
+ /**
+ * @throws IlllegalStateException
+ */
+ private void checkToolbarInitialized() {
+ Preconditions.checkState(mFloatingToolbar != null);
+ Preconditions.checkState(mFloatingToolbarVisibilityHelper != null);
+ }
+
+ private void reset() {
+ mOriginatingView.removeCallbacks(mMovingOff);
+ mOriginatingView.removeCallbacks(mSnoozeOff);
+ }
+
+
+ /**
+ * A helper that shows/hides the floating toolbar depending on certain states.
+ */
+ private static final class FloatingToolbarVisibilityHelper {
+
+ private final FloatingToolbar mToolbar;
+
+ private boolean mSnoozed;
+ private boolean mMoving;
+ private boolean mOutOfBounds;
+
+ public FloatingToolbarVisibilityHelper(FloatingToolbar toolbar) {
+ mToolbar = Preconditions.checkNotNull(toolbar);
+ }
+
+ public void setSnoozed(boolean snoozed) {
+ mSnoozed = snoozed;
+ updateToolbarVisibility();
+ }
+
+ public void setMoving(boolean moving) {
+ mMoving = moving;
+ updateToolbarVisibility();
+ }
+
+ public void setOutOfBounds(boolean outOfBounds) {
+ mOutOfBounds = outOfBounds;
+ updateToolbarVisibility();
+ }
+
+ private void updateToolbarVisibility() {
+ if (mSnoozed || mMoving || mOutOfBounds) {
+ mToolbar.hide();
+ } else if (mToolbar.isHidden()) {
+ mToolbar.show();
+ }
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index fdc3547..b7a53b0 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -357,7 +357,7 @@
mShowAnimation = createGrowFadeInFromBottom(mContentContainer);
mDismissAnimation = createShrinkFadeOutFromBottomAnimation(
mContentContainer,
- 0,
+ 150, // startDelay
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -367,7 +367,7 @@
});
mHideAnimation = createShrinkFadeOutFromBottomAnimation(
mContentContainer,
- 150,
+ 0, // startDelay
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
diff --git a/core/java/com/android/internal/widget/TextViewInputDisabler.java b/core/java/com/android/internal/widget/TextViewInputDisabler.java
new file mode 100644
index 0000000..fb0b3b9
--- /dev/null
+++ b/core/java/com/android/internal/widget/TextViewInputDisabler.java
@@ -0,0 +1,49 @@
+/*
+ * 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.internal.widget;
+
+import android.text.InputFilter;
+import android.text.Spanned;
+import android.widget.TextView;
+
+/**
+ * Helper class to disable input on a TextView. The input is disabled by swapping in an InputFilter
+ * that discards all changes. Use with care if you have customized InputFilter on the target
+ * TextView.
+ */
+public class TextViewInputDisabler {
+ private TextView mTextView;
+ private InputFilter[] mDefaultFilters;
+ private InputFilter[] mNoInputFilters = new InputFilter[] {
+ new InputFilter () {
+ @Override
+ public CharSequence filter(CharSequence source, int start, int end, Spanned dest,
+ int dstart, int dend) {
+ return "";
+ }
+ }
+ };
+
+ public TextViewInputDisabler(TextView textView) {
+ mTextView = textView;
+ mDefaultFilters = mTextView.getFilters();
+ }
+
+ public void setInputEnabled(boolean enabled) {
+ mTextView.setFilters(enabled ? mDefaultFilters : mNoInputFilters);
+ }
+}
diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp
index cf02e39..3bab2df 100644
--- a/core/jni/android/graphics/Region.cpp
+++ b/core/jni/android/graphics/Region.cpp
@@ -212,10 +212,14 @@
android::Parcel* p = android::parcelForJavaObject(env, parcel);
- SkRegion* region = new SkRegion;
- size_t size = p->readInt32();
- size_t actualSize = region->readFromMemory(p->readInplace(size), size);
+ const size_t size = p->readInt32();
+ const void* regionData = p->readInplace(size);
+ if (regionData == nullptr) {
+ return 0;
+ }
+ SkRegion* region = new SkRegion;
+ size_t actualSize = region->readFromMemory(regionData, size);
if (size != actualSize) {
delete region;
return 0;
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 60e8ed0..fada7ac2 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -291,6 +291,11 @@
return (jboolean) !protectFromVpn(socket);
}
+static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId)
+{
+ return (jboolean) !queryUserAccess(uid, netId);
+}
+
// ----------------------------------------------------------------------------
@@ -311,6 +316,7 @@
{ "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
{ "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork },
{ "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
+ { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
{ "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
};
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 0d7d868..21c4a8d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1035,9 +1035,6 @@
<!-- The color applied to framework switch thumbs in their normal state. -->
<attr name="colorSwitchThumbNormal" format="color" />
- <!-- @hide The background used by framework controls. -->
- <attr name="controlBackground" format="reference" />
-
<!-- The color applied to the edge effect on scrolling containers. -->
<attr name="colorEdgeEffect" format="color" />
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index c2371ee..165d1f8 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -572,16 +572,16 @@
<style name="Widget.Material.CompoundButton" parent="Widget.CompoundButton"/>
<style name="Widget.Material.CompoundButton.CheckBox" parent="Widget.CompoundButton.CheckBox">
- <item name="background">?attr/controlBackground</item>
+ <item name="background">@drawable/control_background_material</item>
</style>
<style name="Widget.Material.CompoundButton.RadioButton" parent="Widget.CompoundButton.RadioButton">
- <item name="background">?attr/controlBackground</item>
+ <item name="background">@drawable/control_background_material</item>
</style>
<style name="Widget.Material.CompoundButton.Star" parent="Widget.CompoundButton.Star">
<item name="button">@drawable/btn_star_material</item>
- <item name="background">?attr/controlBackground</item>
+ <item name="background">@drawable/control_background_material</item>
</style>
<style name="Widget.Material.CompoundButton.Switch">
@@ -590,7 +590,7 @@
<item name="switchTextAppearance">@style/TextAppearance.Material.Widget.Switch</item>
<item name="textOn">@string/capital_on</item>
<item name="textOff">@string/capital_off</item>
- <item name="background">?attr/controlBackground</item>
+ <item name="background">@drawable/control_background_material</item>
<item name="showText">false</item>
</style>
@@ -727,7 +727,7 @@
<item name="paddingStart">16dip</item>
<item name="paddingEnd">16dip</item>
<item name="mirrorForRtl">true</item>
- <item name="background">?attr/controlBackground</item>
+ <item name="background">@drawable/control_background_material</item>
</style>
<style name="Widget.Material.RatingBar" parent="Widget.RatingBar">
@@ -809,7 +809,7 @@
</style>
<style name="Widget.Material.Toolbar.Button.Navigation" parent="Widget.Material">
- <item name="background">?attr/controlBackground</item>
+ <item name="background">@drawable/control_background_material</item>
<item name="minWidth">56dp</item>
<item name="scaleType">center</item>
<item name="paddingStart">@dimen/action_bar_navigation_padding_start_material</item>
@@ -866,7 +866,7 @@
</style>
<style name="Widget.Material.ActionButton.CloseMode">
- <item name="background">?attr/controlBackground</item>
+ <item name="background">@drawable/control_background_material</item>
</style>
<style name="Widget.Material.ActionButton.Overflow">
@@ -962,7 +962,7 @@
</style>
<style name="Widget.Material.MediaRouteButton">
- <item name="background">?attr/controlBackground</item>
+ <item name="background">@drawable/control_background_material</item>
<item name="externalRouteEnabledDrawable">@drawable/ic_media_route_material</item>
<item name="minWidth">56dp</item>
<item name="minHeight">48dp</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dee34cc..6124b5b 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -294,7 +294,6 @@
<java-symbol type="bool" name="config_wifi_batched_scan_supported" />
<java-symbol type="bool" name="config_enableMultiUserUI"/>
<java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
- <java-symbol type="bool" name="config_windowIsRound" />
<java-symbol type="bool" name="config_hasRecents" />
<java-symbol type="bool" name="config_windowShowCircularMask" />
<java-symbol type="bool" name="config_windowEnableCircularEmulatorDisplayOverlay" />
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index f02fed1..3f8071f 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -390,8 +390,6 @@
<item name="colorControlHighlight">@color/ripple_material_dark</item>
<item name="colorButtonNormal">@color/btn_default_material_dark</item>
<item name="colorSwitchThumbNormal">@color/switch_thumb_material_dark</item>
-
- <item name="controlBackground">@drawable/control_background_material</item>
</style>
<!-- Material theme (light version). -->
@@ -740,8 +738,6 @@
<item name="colorControlHighlight">@color/ripple_material_light</item>
<item name="colorButtonNormal">@color/btn_default_material_light</item>
<item name="colorSwitchThumbNormal">@color/switch_thumb_material_light</item>
-
- <item name="controlBackground">@drawable/control_background_material</item>
</style>
<!-- Variant of the material (light) theme that has a solid (opaque) action bar
diff --git a/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java b/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
index 0634281..a7bebad 100644
--- a/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
+++ b/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
@@ -26,9 +26,11 @@
import libcore.util.HexEncoding;
import java.net.InetAddress;
+import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.util.Arrays;
import junit.framework.TestCase;
@@ -133,7 +135,7 @@
public static final byte[] RTM_GETNEIGH_RESPONSE =
HexEncoding.decode(RTM_GETNEIGH_RESPONSE_HEX.replaceAll(" ", "").toCharArray(), false);
- public void testParseRtNetlinkNeighborRtmDelNeigh() {
+ public void testParseRtmDelNeigh() {
final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_DELNEIGH);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer);
@@ -159,7 +161,7 @@
assertEquals(InetAddress.parseNumericAddress("192.168.159.254"), destination);
}
- public void testParseRtNetlinkNeighborRtmNewNeigh() {
+ public void testParseRtmNewNeigh() {
final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_NEWNEIGH);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer);
@@ -185,7 +187,7 @@
assertEquals(InetAddress.parseNumericAddress("fe80::86c9:b2ff:fe6a:ed4b"), destination);
}
- public void testParseRtNetlinkNeighborRtmGetNeighResponse() {
+ public void testParseRtmGetNeighResponse() {
final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_GETNEIGH_RESPONSE);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
@@ -208,4 +210,48 @@
// TODO: add more detailed spot checks.
assertEquals(14, messageCount);
}
+
+ public void testCreateRtmNewNeighMessage() {
+ final int seqNo = 2635;
+ final int ifIndex = 14;
+ final byte[] llAddr =
+ new byte[] { (byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5, (byte) 6 };
+
+ // Hexadecimal representation of our created packet.
+ final String expectedNewNeighHex =
+ // struct nlmsghdr
+ "30000000" + // length = 48
+ "1c00" + // type = 28 (RTM_NEWNEIGH)
+ "0101" + // flags (NLM_F_REQUEST | NLM_F_REPLACE)
+ "4b0a0000" + // seqno
+ "00000000" + // pid (0 == kernel)
+ // struct ndmsg
+ "02" + // family
+ "00" + // pad1
+ "0000" + // pad2
+ "0e000000" + // interface index (14)
+ "0800" + // NUD state (0x08 == NUD_DELAY)
+ "00" + // flags
+ "00" + // type
+ // struct nlattr: NDA_DST
+ "0800" + // length = 8
+ "0100" + // type (1 == NDA_DST, for neighbor messages)
+ "7f000001" + // IPv4 address (== 127.0.0.1)
+ // struct nlattr: NDA_LLADDR
+ "0a00" + // length = 10
+ "0200" + // type (2 == NDA_LLADDR, for neighbor messages)
+ "010203040506" + // MAC Address (== 01:02:03:04:05:06)
+ "0000"; // padding, for 4 byte alignment
+ final byte[] expectedNewNeigh =
+ HexEncoding.decode(expectedNewNeighHex.toCharArray(), false);
+
+ final byte[] bytes = RtNetlinkNeighborMessage.newNewNeighborMessage(
+ seqNo, Inet4Address.LOOPBACK, StructNdMsg.NUD_DELAY, ifIndex, llAddr);
+ if (!Arrays.equals(expectedNewNeigh, bytes)) {
+ assertEquals(expectedNewNeigh.length, bytes.length);
+ for (int i = 0; i < Math.min(expectedNewNeigh.length, bytes.length); i++) {
+ assertEquals(expectedNewNeigh[i], bytes[i]);
+ }
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index af6df1a..0b94f8b 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -16,9 +16,13 @@
package android.widget;
+import android.app.Activity;
+import android.content.Intent;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.GetChars;
+import android.text.Selection;
+import android.text.Spannable;
/**
* TextViewTest tests {@link TextView}.
@@ -54,4 +58,66 @@
assertEquals('o', c2[4]);
assertEquals('\0', c2[5]);
}
+
+ public void testProcessTextActivityResultNonEditable() {
+ TextView tv = new TextView(mContext);
+ CharSequence originalText = "This is some text.";
+ tv.setText(originalText, TextView.BufferType.SPANNABLE);
+ assertEquals(originalText, tv.getText().toString());
+ tv.setTextIsSelectable(true);
+ Selection.setSelection((Spannable) tv.getText(), 0, tv.getText().length());
+
+ CharSequence newText = "Text is replaced.";
+ Intent data = new Intent();
+ data.putExtra(Intent.EXTRA_PROCESS_TEXT, newText);
+ tv.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, data);
+
+ // This is a TextView, which can't be modified. Hence no change should have been made.
+ assertEquals(originalText, tv.getText().toString());
+ }
+
+ public void testProcessTextActivityResultEditable() {
+ EditText tv = new EditText(mContext);
+ CharSequence originalText = "This is some text.";
+ tv.setText(originalText, TextView.BufferType.SPANNABLE);
+ assertEquals(originalText, tv.getText().toString());
+ tv.setTextIsSelectable(true);
+ Selection.setSelection(tv.getText(), 0, tv.getText().length());
+
+ CharSequence newText = "Text is replaced.";
+ Intent data = new Intent();
+ data.putExtra(Intent.EXTRA_PROCESS_TEXT, newText);
+ tv.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, data);
+
+ assertEquals(newText, tv.getText().toString());
+ }
+
+ public void testProcessTextActivityResultCancel() {
+ EditText tv = new EditText(mContext);
+ CharSequence originalText = "This is some text.";
+ tv.setText(originalText, TextView.BufferType.SPANNABLE);
+ assertEquals(originalText, tv.getText().toString());
+ tv.setTextIsSelectable(true);
+ Selection.setSelection(tv.getText(), 0, tv.getText().length());
+
+ CharSequence newText = "Text is replaced.";
+ Intent data = new Intent();
+ data.putExtra(Intent.EXTRA_PROCESS_TEXT, newText);
+ tv.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_CANCELED, data);
+
+ assertEquals(originalText, tv.getText().toString());
+ }
+
+ public void testProcessTextActivityNoData() {
+ EditText tv = new EditText(mContext);
+ CharSequence originalText = "This is some text.";
+ tv.setText(originalText, TextView.BufferType.SPANNABLE);
+ assertEquals(originalText, tv.getText().toString());
+ tv.setTextIsSelectable(true);
+ Selection.setSelection(tv.getText(), 0, tv.getText().length());
+
+ tv.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, null);
+
+ assertEquals(originalText, tv.getText().toString());
+ }
}
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 0aa588b..5857de0 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -35,14 +35,14 @@
<familyset>
<family>
<fileset>
- <file variant="elegant">NotoNaskh-Regular.ttf</file>
- <file variant="elegant">NotoNaskh-Bold.ttf</file>
+ <file variant="elegant">NotoNaskhArabic-Regular.ttf</file>
+ <file variant="elegant">NotoNaskhArabic-Bold.ttf</file>
</fileset>
</family>
<family>
<fileset>
- <file variant="compact">NotoNaskhUI-Regular.ttf</file>
- <file variant="compact">NotoNaskhUI-Bold.ttf</file>
+ <file variant="compact">NotoNaskhArabicUI-Regular.ttf</file>
+ <file variant="compact">NotoNaskhArabicUI-Bold.ttf</file>
</fileset>
</family>
<family>
@@ -387,6 +387,11 @@
</family>
<family>
<fileset>
+ <file>NotoSansTibetan-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
<file>NotoSansTifinagh-Regular.ttf</file>
</fileset>
</family>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index f903aab..62da0ff 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -94,12 +94,12 @@
<!-- fallback fonts -->
<family variant="elegant">
- <font weight="400" style="normal">NotoNaskh-Regular.ttf</font>
- <font weight="700" style="normal">NotoNaskh-Bold.ttf</font>
+ <font weight="400" style="normal">NotoNaskhArabic-Regular.ttf</font>
+ <font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font>
</family>
<family variant="compact">
- <font weight="400" style="normal">NotoNaskhUI-Regular.ttf</font>
- <font weight="700" style="normal">NotoNaskhUI-Bold.ttf</font>
+ <font weight="400" style="normal">NotoNaskhArabicUI-Regular.ttf</font>
+ <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
</family>
<family>
<font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
@@ -320,6 +320,9 @@
<font weight="400" style="normal">NotoSansTaiViet-Regular.ttf</font>
</family>
<family>
+ <font weight="400" style="normal">NotoSansTibetan-Regular.ttf</font>
+ </family>
+ <family>
<font weight="400" style="normal">NotoSansTifinagh-Regular.ttf</font>
</family>
<family>
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/docs/html/training/articles/keystore.jd b/docs/html/training/articles/keystore.jd
index 4005a05..20963f5 100644
--- a/docs/html/training/articles/keystore.jd
+++ b/docs/html/training/articles/keystore.jd
@@ -5,6 +5,7 @@
<div id="qv">
<h2>In this document</h2>
<ol>
+ <li><a href="#SecurityFeatures">Security Features</a></li>
<li><a href="#WhichShouldIUse">Choosing Between a Keychain or the Android Keystore Provider</a></li>
<li><a href="#UsingAndroidKeyStore">Using Android Keystore Provider
</a></li>
@@ -31,7 +32,8 @@
keystore, they can be used for cryptographic operations with the key material
remaining non-exportable. Moreover, it offers facilities to restrict when and
how keys can be used, such as requiring user authentication for key use or
- restricting encryption keys to be used only in certain block modes.</p>
+ restricting encryption keys to be used only in certain block modes. See
+ <a href="#SecurityFeatures">Security Features</a> section for more information.</p>
<p>The Keystore system is used by the {@link
android.security.KeyChain} API as well as the Android
@@ -39,6 +41,67 @@
(API level 18). This document goes over when and how to use the
Android Keystore provider.</p>
+
+<h2 id="SecurityFeatures">Security Features</h2>
+
+Android Keystore system protects key material from unauthorized use. Firstly, Android Keystore
+mitigates unauthorized use of key material outside of the Android device by preventing extraction of
+the key material from application processes and from the Android device as a whole. Secondly,
+Android KeyStore mitigates unauthorized use of key material on the Android device by making apps
+specify authorized uses of their keys and then enforcing these restrictions.
+
+<h3 id="ExtractionPrevention">Extraction Prevention</h3>
+
+Key material of Android Keystore keys is protected from extraction using two security measures:
+<ul>
+<li>Key material never enters the application process. When an application performs cryptographic
+ operations using an Android Keystore key, behind the scenes plaintext, ciphertext, and messages to
+ be signed or verified are fed to a system process which carries out the cryptographic operations.
+ If the app's process is compromised, the attacker may be able to use the app's keys but will not
+ be able to extract their key material (for example, to be used outside of the Android device).
+ </li>
+<li>Key material may be bound to the secure hardware (e.g., Trusted Execution Environment (TEE),
+ Secure Element (SE)) of the Android device. When this feature is enabled for a key, its key
+ material is never exposed outside of secure hardware. If the Android OS is compromised or an
+ attacker can read the device's internal storage, the attacker may be able to use any app's Android
+ Keystore keys on the Android device, but not extract them from the device. This feature is enabled
+ only if the device's secure hardware supports the particular combination of key algorithm, block
+ modes, padding schemes, and digests with which the key is authorized to be used. To check whether
+ the feature is enabled for a key, obtain a {@link android.security.keystore.KeyInfo} for the key
+ and inspect the return value of
+ {@link android.security.keystore.KeyInfo#isInsideSecureHardware() KeyInfo.isInsideSecurityHardware()}.
+ </li>
+</ul>
+
+<h3 id="KeyUseAuthorizations">Key Use Authorizations</h3>
+
+To mitigate unauthorized use of keys on the Android device, Android Keystore lets apps specify
+authorized uses of their keys when generating or importing the keys. Once a key is generated or
+imported, its authorizations can not be changed. Authorizations are then enforced by the Android
+Keystore whenever the key is used.
+
+<p>Supported key use authorizations fall into the following categories:
+<ul>
+<li><em>cryptography</em>: authorized key algorithm, operations or purposes (encrypt, decrypt, sign,
+ verify), padding schemes, block modes, digests with which the key can be used</li>
+<li><em>temporal validity interval</em>: interval of time during which the key is authorized for
+ use</li>
+<li><em>user authentication</em>: the key can only be used if the user has been authenticated
+ recently enough. See <a href="#UserAuthentication">Requiring User Authentication For Key Use</a>.
+ </li>
+</ul>
+
+<p>As an additional security measure, for keys whose key material is inside secure hardware (see
+ {@link android.security.keystore.KeyInfo#isInsideSecureHardware() KeyInfo.isInsideSecurityHardware()})
+ some key use authorizations may be enforced by secure hardware, depending on the Android device.
+ Cryptographic and user authentication authorizations are likely to be enforced by secure hardware.
+ Temporal validity interval authorizations are unlikely to be enforced by the secure hardware
+ because it normally does not have an independent secure real-time clock.
+
+<p>Whether a key's user authentication authorization is enforced by the secure hardware can be
+ queried using
+ {@link android.security.keystore.KeyInfo#isUserAuthenticationRequirementEnforcedBySecureHardware() KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware()}.
+
<h2 id="WhichShouldIUse">Choosing Between a Keychain or the
Android Keystore Provider</h2>
@@ -129,7 +192,7 @@
for use as soon as the user unlocks the secure lock screen or confirms their secure lock screen
credentials using the {@link android.app.KeyguardManager#createConfirmDeviceCredentialIntent(CharSequence, CharSequence) KeyguardManager.createConfirmDeviceCredentialIntent}
flow. Each key specifies for how long the authorization remains valid for that key. Such keys
- can only be generated or imported if the secure lock screen is enabled (see {@link android.app.KeyguardManager#isDeviceSecure()}).
+ can only be generated or imported if the secure lock screen is enabled (see {@link android.app.KeyguardManager#isDeviceSecure() KeyguardManager.isDeviceSecure()}).
These keys become permanently invalidated once the secure lock screen is disabled or forcibly
reset (e.g. by a Device Admin).</li>
<li>User authentication is required for every use of the key. In this mode, a specific operation
diff --git a/keystore/java/android/security/keystore/AndroidKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
similarity index 96%
rename from keystore/java/android/security/keystore/AndroidKeyPairGeneratorSpi.java
rename to keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 2c393fd..4b45fd7 100644
--- a/keystore/java/android/security/keystore/AndroidKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -52,19 +52,19 @@
* <p>
* This class can not be directly instantiated and must instead be used via the
* {@link KeyPairGenerator#getInstance(String)
- * KeyPairGenerator.getInstance("AndroidKeyPairGenerator")} API.
+ * KeyPairGenerator.getInstance("AndroidKeyStore")} API.
*
- * {@hide}
+ * @hide
*/
-public abstract class AndroidKeyPairGeneratorSpi extends KeyPairGeneratorSpi {
+public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGeneratorSpi {
- public static class RSA extends AndroidKeyPairGeneratorSpi {
+ public static class RSA extends AndroidKeyStoreKeyPairGeneratorSpi {
public RSA() {
super(KeyProperties.KEY_ALGORITHM_RSA);
}
}
- public static class EC extends AndroidKeyPairGeneratorSpi {
+ public static class EC extends AndroidKeyStoreKeyPairGeneratorSpi {
public EC() {
super(KeyProperties.KEY_ALGORITHM_EC);
}
@@ -94,7 +94,7 @@
private int mKeyType;
private int mKeySize;
- protected AndroidKeyPairGeneratorSpi(@KeyProperties.KeyAlgorithmEnum String algorithm) {
+ protected AndroidKeyStoreKeyPairGeneratorSpi(@KeyProperties.KeyAlgorithmEnum String algorithm) {
mAlgorithm = algorithm;
}
@@ -283,7 +283,8 @@
@Override
public void initialize(int keysize, SecureRandom random) {
- throw new IllegalArgumentException("cannot specify keysize with AndroidKeyPairGenerator");
+ throw new IllegalArgumentException(
+ "cannot specify keysize with AndroidKeyStore KeyPairGenerator");
}
@Override
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index b20a122..649a515 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -49,8 +49,8 @@
put("KeyStore.AndroidKeyStore", PACKAGE_NAME + ".AndroidKeyStoreSpi");
// java.security.KeyPairGenerator
- put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyPairGeneratorSpi$EC");
- put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyPairGeneratorSpi$RSA");
+ put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
+ put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
// javax.crypto.KeyGenerator
put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
@@ -111,6 +111,7 @@
*
* @throws IllegalArgumentException if the provided primitive is not supported or is not backed
* by AndroidKeyStore provider.
+ * @throws IllegalStateException if the provided primitive is not initialized.
*/
public static long getKeyStoreOperationHandle(Object cryptoPrimitive) {
if (cryptoPrimitive == null) {
@@ -118,15 +119,17 @@
}
Object spi;
if (cryptoPrimitive instanceof Mac) {
- spi = ((Mac) cryptoPrimitive).getSpi();
+ spi = ((Mac) cryptoPrimitive).getCurrentSpi();
} else if (cryptoPrimitive instanceof Cipher) {
- spi = ((Cipher) cryptoPrimitive).getSpi();
+ spi = ((Cipher) cryptoPrimitive).getCurrentSpi();
} else {
throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive);
}
- if (!(spi instanceof KeyStoreCryptoOperation)) {
+ if (spi == null) {
+ throw new IllegalStateException("Crypto primitive not initialized");
+ } else if (!(spi instanceof KeyStoreCryptoOperation)) {
throw new IllegalArgumentException(
- "Crypto primitive not backed by AndroidKeyStore: " + cryptoPrimitive
+ "Crypto primitive not backed by AndroidKeyStore provider: " + cryptoPrimitive
+ ", spi: " + spi);
}
return ((KeyStoreCryptoOperation) spi).getOperationHandle();
diff --git a/libs/hwui/utils/RingBuffer.h b/libs/hwui/utils/RingBuffer.h
index 6d0a06b..d822cb2 100644
--- a/libs/hwui/utils/RingBuffer.h
+++ b/libs/hwui/utils/RingBuffer.h
@@ -31,7 +31,7 @@
RingBuffer() {}
~RingBuffer() {}
- constexpr size_t capacity() { return SIZE; }
+ constexpr size_t capacity() const { return SIZE; }
size_t size() { return mCount; }
T& next() {
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/midi/MidiReceiver.java b/media/java/android/media/midi/MidiReceiver.java
index 85c451f..12a5f04 100644
--- a/media/java/android/media/midi/MidiReceiver.java
+++ b/media/java/android/media/midi/MidiReceiver.java
@@ -87,29 +87,38 @@
}
/**
- * Called to send MIDI data to the receiver
+ * Called to send MIDI data to the receiver without a timestamp.
+ * Data will be processed by receiver in the order sent.
* Data will get split into multiple calls to {@link #onSend} if count exceeds
- * {@link #getMaxMessageSize}.
+ * {@link #getMaxMessageSize}. Blocks until all the data is sent or an exception occurs.
+ * In the latter case, the amount of data sent prior to the exception is not provided to caller.
+ * The communication should be considered corrupt. The sender should reestablish
+ * communication, reset all controllers and send all notes off.
*
* @param msg a byte array containing the MIDI data
* @param offset the offset of the first byte of the data in the array to be sent
* @param count the number of bytes of MIDI data in the array to be sent
- * @throws IOException
+ * @throws IOException if the data could not be sent in entirety
*/
public void send(byte[] msg, int offset, int count) throws IOException {
- send(msg, offset, count, System.nanoTime());
+ // TODO add public static final TIMESTAMP_NONE = 0L
+ send(msg, offset, count, 0L);
}
/**
- * Called to send MIDI data to the receiver to be handled at a specified time in the future
+ * Called to send MIDI data to the receiver with a specified timestamp.
+ * Data will be processed by receiver in order first by timestamp, then in the order sent.
* Data will get split into multiple calls to {@link #onSend} if count exceeds
- * {@link #getMaxMessageSize}.
+ * {@link #getMaxMessageSize}. Blocks until all the data is sent or an exception occurs.
+ * In the latter case, the amount of data sent prior to the exception is not provided to caller.
+ * The communication should be considered corrupt. The sender should reestablish
+ * communication, reset all controllers and send all notes off.
*
* @param msg a byte array containing the MIDI data
* @param offset the offset of the first byte of the data in the array to be sent
* @param count the number of bytes of MIDI data in the array to be sent
- * @param timestamp the timestamp of the message (based on {@link java.lang.System#nanoTime}
- * @throws IOException
+ * @param timestamp the timestamp of the message, based on {@link java.lang.System#nanoTime}
+ * @throws IOException if the data could not be sent in entirety
*/
public void send(byte[] msg, int offset, int count, long timestamp)
throws IOException {
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/src/com/android/keyguard/KeyguardPasswordView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
index 3fcc3c3..f18c451 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
@@ -35,6 +35,8 @@
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
+import com.android.internal.widget.TextViewInputDisabler;
+
import java.util.List;
/**
* Displays an alphanumeric (latin-1) key entry for the user to enter
@@ -49,6 +51,8 @@
InputMethodManager mImm;
private TextView mPasswordEntry;
+ private TextViewInputDisabler mPasswordEntryDisabler;
+
private Interpolator mLinearOutSlowInInterpolator;
private Interpolator mFastOutLinearInInterpolator;
@@ -70,7 +74,7 @@
protected void resetState() {
mSecurityMessageDisplay.setMessage(R.string.kg_password_instructions, false);
- mPasswordEntry.setEnabled(true);
+ setPasswordEntryEnabled(true);
}
@Override
@@ -123,6 +127,7 @@
Context.INPUT_METHOD_SERVICE);
mPasswordEntry = (TextView) findViewById(getPasswordTextViewId());
+ mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry);
mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_VARIATION_PASSWORD);
@@ -185,7 +190,7 @@
@Override
protected void setPasswordEntryEnabled(boolean enabled) {
- mPasswordEntry.setEnabled(enabled);
+ mPasswordEntryDisabler.setInputEnabled(enabled);
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 5d6b2f1..d3e7104 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -51,11 +51,13 @@
private final Collection<BluetoothCallback> mCallbacks =
new ArrayList<BluetoothCallback>();
+ private android.os.Handler mReceiverHandler;
+
interface Handler {
void onReceive(Context context, Intent intent, BluetoothDevice device);
}
- void addHandler(String action, Handler handler) {
+ private void addHandler(String action, Handler handler) {
mHandlerMap.put(action, handler);
mAdapterIntentFilter.addAction(action);
}
@@ -103,11 +105,18 @@
// Dock event broadcasts
addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
- mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter);
+ mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
}
void registerProfileIntentReceiver() {
- mContext.registerReceiver(mBroadcastReceiver, mProfileIntentFilter);
+ mContext.registerReceiver(mBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
+ }
+
+ public void setReceiverHandler(android.os.Handler handler) {
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ mReceiverHandler = handler;
+ mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
+ registerProfileIntentReceiver();
}
/** Register to start receiving callbacks for Bluetooth events. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
index 9e2207e..2dc521e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
@@ -105,6 +105,13 @@
return mPanelShowing;
}
+ public void abortCurrentGesture() {
+ if (mStarted) {
+ mStarted = false;
+ mBar.setInteracting(StatusBarManager.WINDOW_NAVIGATION_BAR, false);
+ }
+ }
+
public void setSourceView(View view) {
mSourceView = view;
if (mSourceView != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 7077a17..1dec227 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -245,6 +245,11 @@
return intercept;
}
+ public void abortCurrentGesture() {
+ mDelegateHelper.abortCurrentGesture();
+ getHomeButton().abortCurrentGesture();
+ }
+
private H mHandler = new H();
public View getCurrentView() {
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 2a9df19..9fe591e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1013,7 +1013,7 @@
invokeAssistGesture(true /* vibrate */);
awakenDreams();
if (mNavigationBarView != null) {
- mNavigationBarView.getHomeButton().abortCurrentGesture();
+ mNavigationBarView.abortCurrentGesture();
}
}
};
@@ -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/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
index 5893cb2..0eb7197 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -45,7 +45,7 @@
private static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid";
private static final int[] ICONS = {
- R.drawable.ic_qs_wifi_0,
+ R.drawable.ic_qs_wifi_full_0,
R.drawable.ic_qs_wifi_full_1,
R.drawable.ic_qs_wifi_full_2,
R.drawable.ic_qs_wifi_full_3,
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 8d4f302..114427c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -18,7 +18,9 @@
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
+import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
import android.util.Log;
import com.android.settingslib.bluetooth.BluetoothCallback;
@@ -42,9 +44,12 @@
private boolean mConnecting;
private CachedBluetoothDevice mLastDevice;
+ private final H mHandler = new H();
+
public BluetoothControllerImpl(Context context, Looper bgLooper) {
mLocalBluetoothManager = LocalBluetoothManager.getInstance(context, null);
if (mLocalBluetoothManager != null) {
+ mLocalBluetoothManager.getEventManager().setReceiverHandler(new Handler(bgLooper));
mLocalBluetoothManager.getEventManager().registerCallback(this);
onBluetoothStateChanged(
mLocalBluetoothManager.getBluetoothAdapter().getBluetoothState());
@@ -71,7 +76,7 @@
public void addStateChangedCallback(Callback cb) {
mCallbacks.add(cb);
- fireStateChange(cb);
+ mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
@Override
@@ -132,22 +137,6 @@
: null;
}
- private void firePairedDevicesChanged() {
- for (Callback cb : mCallbacks) {
- cb.onBluetoothDevicesChanged();
- }
- }
-
- private void fireStateChange() {
- for (Callback cb : mCallbacks) {
- fireStateChange(cb);
- }
- }
-
- private void fireStateChange(Callback cb) {
- cb.onBluetoothStateChange(mEnabled, mConnecting);
- }
-
private void updateConnected() {
if (mLastDevice != null && mLastDevice.isConnected()) {
// Our current device is still valid.
@@ -163,7 +152,7 @@
@Override
public void onBluetoothStateChanged(int bluetoothState) {
mEnabled = bluetoothState == BluetoothAdapter.STATE_ON;
- fireStateChange();
+ mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
@Override
@@ -175,25 +164,25 @@
public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
cachedDevice.registerCallback(this);
updateConnected();
- firePairedDevicesChanged();
+ mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
updateConnected();
- firePairedDevicesChanged();
+ mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
updateConnected();
- firePairedDevicesChanged();
+ mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
public void onDeviceAttributesChanged() {
updateConnected();
- firePairedDevicesChanged();
+ mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
@@ -201,6 +190,39 @@
mConnecting = state == BluetoothAdapter.STATE_CONNECTING;
mLastDevice = cachedDevice;
updateConnected();
- fireStateChange();
+ mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
+ }
+
+ private final class H extends Handler {
+ private static final int MSG_PAIRED_DEVICES_CHANGED = 1;
+ private static final int MSG_STATE_CHANGED = 2;
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_PAIRED_DEVICES_CHANGED:
+ firePairedDevicesChanged();
+ break;
+ case MSG_STATE_CHANGED:
+ fireStateChange();
+ break;
+ }
+ }
+
+ private void firePairedDevicesChanged() {
+ for (BluetoothController.Callback cb : mCallbacks) {
+ cb.onBluetoothDevicesChanged();
+ }
+ }
+
+ private void fireStateChange() {
+ for (BluetoothController.Callback cb : mCallbacks) {
+ fireStateChange(cb);
+ }
+ }
+
+ private void fireStateChange(BluetoothController.Callback cb) {
+ cb.onBluetoothStateChange(mEnabled, mConnecting);
+ }
}
}
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/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 6e0ca3c..1c6462e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -724,7 +724,7 @@
}
row.slider.setProgress(newProgress);
}
- if (mAutomute) {
+ if (mAutomute && mShowing) {
if (vlevel == 0 && !row.ss.muted && row.stream == AudioManager.STREAM_MUSIC) {
mController.setStreamMute(row.stream, true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
index 3a8081f..c6d9e46 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
@@ -180,7 +180,7 @@
pw.print(" mEnabled: "); pw.println(mEnabled);
pw.print(" mDestroyed: "); pw.println(mDestroyed);
pw.print(" mVolumePolicy: "); pw.println(mVolumePolicy);
- pw.print(" mEnabled: "); pw.println(mEnabled);
+ pw.print(" mState: "); pw.println(mState.toString(4));
pw.print(" mShowDndTile: "); pw.println(mShowDndTile);
pw.print(" mHasVibrator: "); pw.println(mHasVibrator);
pw.print(" mRemoteStreams: "); pw.println(mMediaSessionsCallbacksW.mRemoteStreams
@@ -960,25 +960,45 @@
@Override
public String toString() {
+ return toString(0);
+ }
+
+ public String toString(int indent) {
final StringBuilder sb = new StringBuilder("{");
+ if (indent > 0) sep(sb, indent);
for (int i = 0; i < states.size(); i++) {
- if (i > 0) sb.append(',');
+ if (i > 0) {
+ sep(sb, indent);
+ }
final int stream = states.keyAt(i);
final StreamState ss = states.valueAt(i);
sb.append(AudioSystem.streamToString(stream)).append(":").append(ss.level)
- .append("[").append(ss.levelMin).append("..").append(ss.levelMax);
+ .append('[').append(ss.levelMin).append("..").append(ss.levelMax)
+ .append(']');
if (ss.muted) sb.append(" [MUTED]");
}
- sb.append(",ringerModeExternal:").append(ringerModeExternal);
- sb.append(",ringerModeInternal:").append(ringerModeInternal);
- sb.append(",zenMode:").append(zenMode);
- sb.append(",effectsSuppressor:").append(effectsSuppressor);
- sb.append(",effectsSuppressorName:").append(effectsSuppressorName);
- sb.append(",zenModeConfig:").append(zenModeConfig);
- sb.append(",activeStream:").append(activeStream);
+ sep(sb, indent); sb.append("ringerModeExternal:").append(ringerModeExternal);
+ sep(sb, indent); sb.append("ringerModeInternal:").append(ringerModeInternal);
+ sep(sb, indent); sb.append("zenMode:").append(zenMode);
+ sep(sb, indent); sb.append("effectsSuppressor:").append(effectsSuppressor);
+ sep(sb, indent); sb.append("effectsSuppressorName:").append(effectsSuppressorName);
+ sep(sb, indent); sb.append("zenModeConfig:").append(zenModeConfig);
+ sep(sb, indent); sb.append("activeStream:").append(activeStream);
+ if (indent > 0) sep(sb, indent);
return sb.append('}').toString();
}
+ private static void sep(StringBuilder sb, int indent) {
+ if (indent > 0) {
+ sb.append('\n');
+ for (int i = 0; i < indent; i++) {
+ sb.append(' ');
+ }
+ } else {
+ sb.append(',');
+ }
+ }
+
public Condition getManualExitCondition() {
return zenModeConfig != null && zenModeConfig.manualRule != null
? zenModeConfig.manualRule.condition : null;
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index ea8b2ec..48e0582 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -21,6 +21,7 @@
import android.graphics.drawable.Drawable;
import android.net.IConnectivityManager;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.text.Html;
import android.text.Html.ImageGetter;
import android.util.Log;
@@ -50,7 +51,7 @@
mService = IConnectivityManager.Stub.asInterface(
ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
- if (mService.prepareVpn(mPackage, null)) {
+ if (mService.prepareVpn(mPackage, null, UserHandle.myUserId())) {
setResult(RESULT_OK);
finish();
return;
@@ -94,10 +95,10 @@
@Override
public void onClick(DialogInterface dialog, int which) {
try {
- if (mService.prepareVpn(null, mPackage)) {
+ if (mService.prepareVpn(null, mPackage, UserHandle.myUserId())) {
// Authorize this app to initiate VPN connections in the future without user
// intervention.
- mService.setVpnPackageAuthorization(true);
+ mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), true);
setResult(RESULT_OK);
}
} catch (Exception e) {
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index cc8500a..76b2346 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -23,6 +23,7 @@
import android.os.Message;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
@@ -63,7 +64,7 @@
mService = IConnectivityManager.Stub.asInterface(
ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
- mConfig = mService.getVpnConfig();
+ mConfig = mService.getVpnConfig(UserHandle.myUserId());
// mConfig can be null if we are a restricted user, in that case don't show this dialog
if (mConfig == null) {
@@ -120,10 +121,11 @@
if (which == DialogInterface.BUTTON_POSITIVE) {
mConfig.configureIntent.send();
} else if (which == DialogInterface.BUTTON_NEUTRAL) {
+ final int myUserId = UserHandle.myUserId();
if (mConfig.legacy) {
- mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+ mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
} else {
- mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN);
+ mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
}
}
} catch (Exception e) {
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/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 02bffc4..7ac2655 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1071,23 +1071,6 @@
}
}
- private NetworkCapabilities getNetworkCapabilitiesAndValidation(NetworkAgentInfo nai) {
- if (nai != null) {
- synchronized (nai) {
- if (nai.created) {
- NetworkCapabilities nc = new NetworkCapabilities(nai.networkCapabilities);
- if (nai.lastValidated) {
- nc.addCapability(NET_CAPABILITY_VALIDATED);
- } else {
- nc.removeCapability(NET_CAPABILITY_VALIDATED);
- }
- return nc;
- }
- }
- }
- return null;
- }
-
@Override
public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
// The basic principle is: if an app's traffic could possibly go over a
@@ -1109,7 +1092,7 @@
HashMap<Network, NetworkCapabilities> result = new HashMap<Network, NetworkCapabilities>();
NetworkAgentInfo nai = getDefaultNetwork();
- NetworkCapabilities nc = getNetworkCapabilitiesAndValidation(getDefaultNetwork());
+ NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
if (nc != null) {
result.put(nai.network, nc);
}
@@ -1122,9 +1105,9 @@
if (networks != null) {
for (Network network : networks) {
nai = getNetworkAgentInfoForNetwork(network);
- nc = getNetworkCapabilitiesAndValidation(nai);
+ nc = getNetworkCapabilitiesInternal(nai);
if (nc != null) {
- result.put(nai.network, nc);
+ result.put(network, nc);
}
}
}
@@ -1184,25 +1167,24 @@
return null;
}
- @Override
- public NetworkCapabilities getNetworkCapabilities(Network network) {
- enforceAccessPermission();
- NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+ private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) {
if (nai != null) {
synchronized (nai) {
- NetworkCapabilities nc = new NetworkCapabilities(nai.networkCapabilities);
- if (nai.lastValidated) {
- nc.addCapability(NET_CAPABILITY_VALIDATED);
- } else {
- nc.removeCapability(NET_CAPABILITY_VALIDATED);
+ if (nai.networkCapabilities != null) {
+ return new NetworkCapabilities(nai.networkCapabilities);
}
- return nc;
}
}
return null;
}
@Override
+ public NetworkCapabilities getNetworkCapabilities(Network network) {
+ enforceAccessPermission();
+ return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
+ }
+
+ @Override
public NetworkState[] getAllNetworkState() {
// Require internal since we're handing out IMSI details
enforceConnectivityInternalPermission();
@@ -1410,6 +1392,22 @@
}
};
+ /**
+ * Require that the caller is either in the same user or has appropriate permission to interact
+ * across users.
+ *
+ * @param userId Target user for whatever operation the current IPC is supposed to perform.
+ */
+ private void enforceCrossUserPermission(int userId) {
+ if (userId == UserHandle.getCallingUserId()) {
+ // Not a cross-user call.
+ return;
+ }
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "ConnectivityService");
+ }
+
private void enforceInternetPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERNET,
@@ -1950,11 +1948,14 @@
}
case NetworkMonitor.EVENT_NETWORK_TESTED: {
NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
- if (isLiveNetworkAgent(nai, "EVENT_NETWORK_VALIDATED")) {
- boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
+ if (isLiveNetworkAgent(nai, "EVENT_NETWORK_TESTED")) {
+ final boolean valid =
+ (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
+ final boolean validationChanged = (valid != nai.lastValidated);
nai.lastValidated = valid;
if (valid) {
if (DBG) log("Validated " + nai.name());
+ nai.networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED);
if (!nai.everValidated) {
nai.everValidated = true;
rematchNetworkAndRequests(nai, NascentState.JUST_VALIDATED,
@@ -1962,6 +1963,8 @@
// If score has changed, rebroadcast to NetworkFactories. b/17726566
sendUpdatedScoreToFactories(nai);
}
+ } else {
+ nai.networkCapabilities.removeCapability(NET_CAPABILITY_VALIDATED);
}
updateInetCondition(nai);
// Let the NetworkAgent know the state of its network
@@ -1970,8 +1973,9 @@
(valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
0, null);
- // TODO: trigger a NetworkCapabilities update so that the dialog can know
- // that the network is now validated and close itself.
+ if (validationChanged) {
+ notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
+ }
}
break;
}
@@ -2684,7 +2688,7 @@
actionToken);
}
- public ProxyInfo getDefaultProxy() {
+ private ProxyInfo getDefaultProxy() {
// this information is already available as a world read/writable jvm property
// so this API change wouldn't have a benifit. It also breaks the passing
// of proxy info to all the JVMs.
@@ -2696,6 +2700,22 @@
}
}
+ public ProxyInfo getProxyForNetwork(Network network) {
+ if (network == null) return getDefaultProxy();
+ final ProxyInfo globalProxy = getGlobalProxy();
+ if (globalProxy != null) return globalProxy;
+ if (!NetworkUtils.queryUserAccess(Binder.getCallingUid(), network.netId)) return null;
+ // Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
+ // caller may not have.
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+ if (nai == null) return null;
+ synchronized (nai) {
+ final ProxyInfo proxyInfo = nai.linkProperties.getHttpProxy();
+ if (proxyInfo == null) return null;
+ return new ProxyInfo(proxyInfo);
+ }
+ }
+
// Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
// (e.g. if mGlobalProxy==null fall back to network-specific proxy, if network-specific
// proxy is null then there is no proxy in place).
@@ -2945,29 +2965,48 @@
/**
* Prepare for a VPN application.
- * Permissions are checked in Vpn class.
+ * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
+ * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ *
+ * @param oldPackage Package name of the application which currently controls VPN, which will
+ * be replaced. If there is no such application, this should should either be
+ * {@code null} or {@link VpnConfig.LEGACY_VPN}.
+ * @param newPackage Package name of the application which should gain control of VPN, or
+ * {@code null} to disable.
+ * @param userId User for whom to prepare the new VPN.
+ *
* @hide
*/
@Override
- public boolean prepareVpn(String oldPackage, String newPackage) {
+ public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
+ int userId) {
+ enforceCrossUserPermission(userId);
throwIfLockdownEnabled();
- int user = UserHandle.getUserId(Binder.getCallingUid());
+
synchronized(mVpns) {
- return mVpns.get(user).prepare(oldPackage, newPackage);
+ return mVpns.get(userId).prepare(oldPackage, newPackage);
}
}
/**
- * Set whether the current VPN package has the ability to launch VPNs without
- * user intervention. This method is used by system-privileged apps.
- * Permissions are checked in Vpn class.
+ * Set whether the VPN package has the ability to launch VPNs without user intervention.
+ * This method is used by system-privileged apps.
+ * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
+ * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ *
+ * @param packageName The package for which authorization state should change.
+ * @param userId User for whom {@code packageName} is installed.
+ * @param authorized {@code true} if this app should be able to start a VPN connection without
+ * explicit user approval, {@code false} if not.
+ *
* @hide
*/
@Override
- public void setVpnPackageAuthorization(boolean authorized) {
- int user = UserHandle.getUserId(Binder.getCallingUid());
+ public void setVpnPackageAuthorization(String packageName, int userId, boolean authorized) {
+ enforceCrossUserPermission(userId);
+
synchronized(mVpns) {
- mVpns.get(user).setPackageAuthorization(authorized);
+ mVpns.get(userId).setPackageAuthorization(packageName, authorized);
}
}
@@ -3069,16 +3108,16 @@
}
/**
- * Returns the information of the ongoing VPN. This method is used by VpnDialogs and
- * not available in ConnectivityManager.
+ * Returns the information of the ongoing VPN for {@code userId}. This method is used by
+ * VpnDialogs and not available in ConnectivityManager.
* Permissions are checked in Vpn class.
* @hide
*/
@Override
- public VpnConfig getVpnConfig() {
- int user = UserHandle.getUserId(Binder.getCallingUid());
+ public VpnConfig getVpnConfig(int userId) {
+ enforceCrossUserPermission(userId);
synchronized(mVpns) {
- return mVpns.get(user).getVpnConfig();
+ return mVpns.get(userId).getVpnConfig();
}
}
@@ -3534,8 +3573,7 @@
}
private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities) {
- if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
- == false) {
+ if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) == false) {
enforceConnectivityInternalPermission();
} else {
enforceChangePermission();
@@ -3562,8 +3600,7 @@
private void enforceMeteredApnPolicy(NetworkCapabilities networkCapabilities) {
// if UID is restricted, don't allow them to bring up metered APNs
- if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
- == false) {
+ if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED) == false) {
final int uidRules;
final int uid = Binder.getCallingUid();
synchronized(mRulesLock) {
@@ -3934,6 +3971,11 @@
synchronized (networkAgent) {
networkAgent.networkCapabilities = networkCapabilities;
}
+ if (networkAgent.lastValidated) {
+ networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED);
+ // There's no need to remove the capability if we think the network is unvalidated,
+ // because NetworkAgents don't set the validated capability.
+ }
rematchAllNetworksAndRequests(networkAgent, networkAgent.getCurrentScore());
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
@@ -4560,6 +4602,8 @@
@Override
public void factoryReset() {
enforceConnectivityInternalPermission();
+ final int userId = UserHandle.getCallingUserId();
+
// Turn airplane mode off
setAirplaneMode(false);
@@ -4569,16 +4613,16 @@
}
// Turn VPN off
- VpnConfig vpnConfig = getVpnConfig();
+ VpnConfig vpnConfig = getVpnConfig(userId);
if (vpnConfig != null) {
if (vpnConfig.legacy) {
- prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+ prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
} else {
- // Prevent this app from initiating VPN connections in the future without
- // user intervention.
- setVpnPackageAuthorization(false);
+ // Prevent this app (packagename = vpnConfig.user) from initiating VPN connections
+ // in the future without user intervention.
+ setVpnPackageAuthorization(vpnConfig.user, userId, false);
- prepareVpn(vpnConfig.user, VpnConfig.LEGACY_VPN);
+ prepareVpn(vpnConfig.user, VpnConfig.LEGACY_VPN, userId);
}
}
}
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/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 925fae0a..ec2bd67 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -71,6 +71,7 @@
static final boolean DEBUG_TASKS = DEBUG_ALL || false;
static final boolean DEBUG_THUMBNAILS = DEBUG_ALL || false;
static final boolean DEBUG_TRANSITION = DEBUG_ALL || false;
+ static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false;
static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
@@ -104,6 +105,8 @@
static final String POSTFIX_TASKS = (APPEND_CATEGORY_NAME) ? "_Tasks" : "";
static final String POSTFIX_THUMBNAILS = (APPEND_CATEGORY_NAME) ? "_Thumbnails" : "";
static final String POSTFIX_TRANSITION = (APPEND_CATEGORY_NAME) ? "_Transition" : "";
+ static final String POSTFIX_UID_OBSERVERS = (APPEND_CATEGORY_NAME)
+ ? "_UidObservers" : "";
static final String POSTFIX_URI_PERMISSION = (APPEND_CATEGORY_NAME) ? "_UriPermission" : "";
static final String POSTFIX_USER_LEAVING = (APPEND_CATEGORY_NAME) ? "_UserLeaving" : "";
static final String POSTFIX_VISIBILITY = (APPEND_CATEGORY_NAME) ? "_Visibility" : "";
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2d74618..6842304 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -129,6 +129,7 @@
import android.app.IProcessObserver;
import android.app.IServiceConnection;
import android.app.IStopUserCallback;
+import android.app.IUidObserver;
import android.app.IUiAutomationConnection;
import android.app.IUserSwitchObserver;
import android.app.Instrumentation;
@@ -253,7 +254,6 @@
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- private static final String USER_DATA_DIR = "/data/user/";
// File that stores last updated system version and called preboot receivers
static final String CALLED_PRE_BOOTS_FILENAME = "called_pre_boots.dat";
@@ -278,6 +278,7 @@
private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
private static final String TAG_STACK = TAG + POSTFIX_STACK;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+ private static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
private static final String TAG_URI_PERMISSION = TAG + POSTFIX_URI_PERMISSION;
private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
@@ -655,9 +656,14 @@
long mPreviousProcessVisibleTime;
/**
+ * Track all uids that have actively running processes.
+ */
+ final SparseArray<UidRecord> mActiveUids = new SparseArray<>();
+
+ /**
* Which uses have been started, so are allowed to run code.
*/
- final SparseArray<UserStartedState> mStartedUsers = new SparseArray<UserStartedState>();
+ final SparseArray<UserStartedState> mStartedUsers = new SparseArray<>();
/**
* LRU list of history of current users. Most recently current is at the end.
@@ -1023,6 +1029,11 @@
private IVoiceInteractionSession mRunningVoice;
/**
+ * For some direct access we need to power manager.
+ */
+ PowerManagerInternal mLocalPowerManager;
+
+ /**
* We want to hold a wake lock while running a voice interaction session, since
* this may happen with the screen off and we need to keep the CPU running to
* be able to continue to interact with the user.
@@ -1174,7 +1185,7 @@
final long[] mTmpLong = new long[1];
- static class ProcessChangeItem {
+ static final class ProcessChangeItem {
static final int CHANGE_ACTIVITIES = 1<<0;
static final int CHANGE_PROCESS_STATE = 1<<1;
int changes;
@@ -1184,14 +1195,17 @@
boolean foregroundActivities;
}
- final RemoteCallbackList<IProcessObserver> mProcessObservers
- = new RemoteCallbackList<IProcessObserver>();
+ final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
- final ArrayList<ProcessChangeItem> mPendingProcessChanges
- = new ArrayList<ProcessChangeItem>();
- final ArrayList<ProcessChangeItem> mAvailProcessChanges
- = new ArrayList<ProcessChangeItem>();
+ final ArrayList<ProcessChangeItem> mPendingProcessChanges = new ArrayList<>();
+ final ArrayList<ProcessChangeItem> mAvailProcessChanges = new ArrayList<>();
+
+ final RemoteCallbackList<IUidObserver> mUidObservers = new RemoteCallbackList<>();
+ UidRecord.ChangeItem[] mActiveUidChanges = new UidRecord.ChangeItem[5];
+
+ final ArrayList<UidRecord.ChangeItem> mPendingUidChanges = new ArrayList<>();
+ final ArrayList<UidRecord.ChangeItem> mAvailUidChanges = new ArrayList<>();
/**
* Runtime CPU use collection thread. This object's lock is used to
@@ -1316,6 +1330,7 @@
static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 51;
static final int DELETE_DUMPHEAP_MSG = 52;
static final int FOREGROUND_PROFILE_CHANGED_MSG = 53;
+ static final int DISPATCH_UIDS_CHANGED = 54;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1538,6 +1553,19 @@
d.dismiss();
break;
}
+ case DISPATCH_PROCESSES_CHANGED: {
+ dispatchProcessesChanged();
+ break;
+ }
+ case DISPATCH_PROCESS_DIED: {
+ final int pid = msg.arg1;
+ final int uid = msg.arg2;
+ dispatchProcessDied(pid, uid);
+ break;
+ }
+ case DISPATCH_UIDS_CHANGED: {
+ dispatchUidsChanged();
+ } break;
}
}
}
@@ -1723,16 +1751,6 @@
sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
}
} break;
- case DISPATCH_PROCESSES_CHANGED: {
- dispatchProcessesChanged();
- break;
- }
- case DISPATCH_PROCESS_DIED: {
- final int pid = msg.arg1;
- final int uid = msg.arg2;
- dispatchProcessDied(pid, uid);
- break;
- }
case REPORT_MEM_USAGE_MSG: {
final ArrayList<ProcessMemInfo> memInfos = (ArrayList<ProcessMemInfo>)msg.obj;
Thread thread = new Thread() {
@@ -2091,7 +2109,6 @@
app.pid = MY_PID;
app.maxAdj = ProcessList.SYSTEM_ADJ;
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
- mProcessNames.put(app.processName, app.uid, app);
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.put(app.pid, app);
}
@@ -2343,6 +2360,7 @@
public void initPowerManagement() {
mStackSupervisor.initPowerManagement();
mBatteryStatsService.initPowerManagement();
+ mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
mVoiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*voice*");
mVoiceWakeLock.setReferenceCounted(false);
@@ -3073,10 +3091,6 @@
return null;
}
app.crashHandler = crashHandler;
- mProcessNames.put(processName, app.uid, app);
- if (isolated) {
- mIsolatedProcesses.put(app.uid, app);
- }
checkTime(startTime, "startProcess: done creating new process record");
} else {
// If this is a new package in the process, add the package to the list
@@ -3562,7 +3576,6 @@
mActiveProcessChanges = new ProcessChangeItem[N];
}
mPendingProcessChanges.toArray(mActiveProcessChanges);
- mAvailProcessChanges.addAll(mPendingProcessChanges);
mPendingProcessChanges.clear();
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"*** Delivering " + N + " process changes");
@@ -3595,6 +3608,12 @@
}
}
mProcessObservers.finishBroadcast();
+
+ synchronized (this) {
+ for (int j=0; j<N; j++) {
+ mAvailProcessChanges.add(mActiveProcessChanges[j]);
+ }
+ }
}
private void dispatchProcessDied(int pid, int uid) {
@@ -3612,6 +3631,67 @@
mProcessObservers.finishBroadcast();
}
+ private void dispatchUidsChanged() {
+ int N;
+ synchronized (this) {
+ N = mPendingUidChanges.size();
+ if (mActiveUidChanges.length < N) {
+ mActiveUidChanges = new UidRecord.ChangeItem[N];
+ }
+ for (int i=0; i<N; i++) {
+ final UidRecord.ChangeItem change = mPendingUidChanges.get(i);
+ mActiveUidChanges[i] = change;
+ change.uidRecord.pendingChange = null;
+ change.uidRecord = null;
+ }
+ mPendingUidChanges.clear();
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "*** Delivering " + N + " uid changes");
+ }
+
+ if (mLocalPowerManager != null) {
+ for (int j=0; j<N; j++) {
+ UidRecord.ChangeItem item = mActiveUidChanges[j];
+ if (item.gone) {
+ mLocalPowerManager.uidGone(item.uid);
+ } else {
+ mLocalPowerManager.updateUidProcState(item.uid, item.processState);
+ }
+ }
+ }
+
+ int i = mUidObservers.beginBroadcast();
+ while (i > 0) {
+ i--;
+ final IUidObserver observer = mUidObservers.getBroadcastItem(i);
+ if (observer != null) {
+ try {
+ for (int j=0; j<N; j++) {
+ UidRecord.ChangeItem item = mActiveUidChanges[j];
+ if (item.gone) {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "UID gone uid=" + item.uid);
+ observer.onUidGone(item.uid);
+ } else {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "UID CHANGED uid=" + item.uid
+ + ": " + item.processState);
+ observer.onUidStateChanged(item.uid, item.processState);
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mUidObservers.finishBroadcast();
+
+ synchronized (this) {
+ for (int j=0; j<N; j++) {
+ mAvailUidChanges.add(mActiveUidChanges[j]);
+ }
+ }
+ }
+
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
@@ -5623,6 +5703,47 @@
return didSomething;
}
+ private final ProcessRecord removeProcessNameLocked(final String name, final int uid) {
+ ProcessRecord old = mProcessNames.remove(name, uid);
+ if (old != null) {
+ old.uidRecord.numProcs--;
+ if (old.uidRecord.numProcs == 0) {
+ // No more processes using this uid, tell clients it is gone.
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "No more processes in " + old.uidRecord);
+ enqueueUidChangeLocked(old.uidRecord, true);
+ mActiveUids.remove(uid);
+ }
+ old.uidRecord = null;
+ }
+ mIsolatedProcesses.remove(uid);
+ return old;
+ }
+
+ private final void addProcessNameLocked(ProcessRecord proc) {
+ // We shouldn't already have a process under this name, but just in case we
+ // need to clean up whatever may be there now.
+ ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
+ if (old != null) {
+ Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+ }
+ UidRecord uidRec = mActiveUids.get(proc.uid);
+ if (uidRec == null) {
+ uidRec = new UidRecord(proc.uid);
+ // This is the first appearance of the uid, report it now!
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "Creating new process uid: " + uidRec);
+ mActiveUids.put(proc.uid, uidRec);
+ enqueueUidChangeLocked(uidRec, false);
+ }
+ proc.uidRecord = uidRec;
+ uidRec.numProcs++;
+ mProcessNames.put(proc.processName, proc.uid, proc);
+ if (proc.isolated) {
+ mIsolatedProcesses.put(proc.uid, proc);
+ }
+ }
+
private final boolean removeProcessLocked(ProcessRecord app,
boolean callerWillRestart, boolean allowRestart, String reason) {
final String name = app.processName;
@@ -5630,8 +5751,7 @@
if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES,
"Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")");
- mProcessNames.remove(name, uid);
- mIsolatedProcesses.remove(app.uid);
+ removeProcessNameLocked(name, uid);
if (mHeavyWeightProcess == app) {
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
mHeavyWeightProcess.userId, 0));
@@ -5684,8 +5804,7 @@
Slog.w(TAG, "Process " + app + " failed to attach");
EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, app.userId,
pid, app.uid, app.processName);
- mProcessNames.remove(app.processName, app.uid);
- mIsolatedProcesses.remove(app.uid);
+ removeProcessNameLocked(app.processName, app.uid);
if (mHeavyWeightProcess == app) {
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
mHeavyWeightProcess.userId, 0));
@@ -9874,7 +9993,6 @@
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated, int isolatedUid) {
String proc = customProcess != null ? customProcess : info.processName;
- BatteryStatsImpl.Uid.Proc ps = null;
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
final int userId = UserHandle.getUserId(info.uid);
int uid = info.uid;
@@ -9909,6 +10027,7 @@
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
r.persistent = true;
}
+ addProcessNameLocked(r);
return r;
}
@@ -9923,10 +10042,6 @@
if (app == null) {
app = newProcessRecordLocked(info, null, isolated, 0);
- mProcessNames.put(info.processName, app.uid, app);
- if (isolated) {
- mIsolatedProcesses.put(app.uid, app);
- }
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
@@ -10602,6 +10717,21 @@
}
}
+ public void registerUidObserver(IUidObserver observer) {
+ enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+ "registerUidObserver()");
+ synchronized (this) {
+ mUidObservers.register(observer);
+ }
+ }
+
+ @Override
+ public void unregisterUidObserver(IUidObserver observer) {
+ synchronized (this) {
+ mUidObservers.unregister(observer);
+ }
+ }
+
@Override
public boolean convertFromTranslucent(IBinder token) {
final long origId = Binder.clearCallingIdentity();
@@ -12920,6 +13050,20 @@
}
}
+ if (mActiveUids.size() > 0) {
+ if (needSep) {
+ pw.println();
+ }
+ pw.println(" UID states:");
+ for (int i=0; i<mActiveUids.size(); i++) {
+ UidRecord uidRec = mActiveUids.valueAt(i);
+ pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid);
+ pw.print(": "); pw.println(uidRec);
+ }
+ needSep = true;
+ printedAnything = true;
+ }
+
if (mLruProcesses.size() > 0) {
if (needSep) {
pw.println();
@@ -15162,7 +15306,7 @@
mAvailProcessChanges.add(item);
}
}
- mHandler.obtainMessage(DISPATCH_PROCESS_DIED, app.pid, app.info.uid, null).sendToTarget();
+ mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED, app.pid, app.info.uid, null).sendToTarget();
// If the caller is restarting this app, then leave it in its
// current lists and let the caller take care of it.
@@ -15173,8 +15317,7 @@
if (!app.persistent || app.isolated) {
if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
"Removing non-persistent process during cleanup: " + app);
- mProcessNames.remove(app.processName, app.uid);
- mIsolatedProcesses.remove(app.uid);
+ removeProcessNameLocked(app.processName, app.uid);
if (mHeavyWeightProcess == app) {
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
mHeavyWeightProcess.userId, 0));
@@ -15206,7 +15349,7 @@
if (index < 0) {
ProcessList.remove(app.pid);
}
- mProcessNames.put(app.processName, app.uid, app);
+ addProcessNameLocked(app);
startProcessLocked(app, "restart", app.processName);
return true;
} else if (app.pid > 0 && app.pid != MY_PID) {
@@ -18289,7 +18432,7 @@
if (NA > 0) {
item = mAvailProcessChanges.remove(NA-1);
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
- "Retreiving available item: " + item);
+ "Retrieving available item: " + item);
} else {
item = new ProcessChangeItem();
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
@@ -18301,7 +18444,7 @@
if (mPendingProcessChanges.size() == 0) {
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"*** Enqueueing dispatch processes changed!");
- mHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED).sendToTarget();
+ mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED).sendToTarget();
}
mPendingProcessChanges.add(item);
}
@@ -18320,6 +18463,31 @@
return success;
}
+ private final void enqueueUidChangeLocked(UidRecord uidRec, boolean gone) {
+ if (uidRec.pendingChange == null) {
+ if (mPendingUidChanges.size() == 0) {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "*** Enqueueing dispatch uid changed!");
+ mUiHandler.obtainMessage(DISPATCH_UIDS_CHANGED).sendToTarget();
+ }
+ final int NA = mAvailUidChanges.size();
+ if (NA > 0) {
+ uidRec.pendingChange = mAvailUidChanges.remove(NA-1);
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "Retrieving available item: " + uidRec.pendingChange);
+ } else {
+ uidRec.pendingChange = new UidRecord.ChangeItem();
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "Allocating new item: " + uidRec.pendingChange);
+ }
+ uidRec.pendingChange.uidRecord = uidRec;
+ uidRec.pendingChange.uid = uidRec.uid;
+ mPendingUidChanges.add(uidRec.pendingChange);
+ }
+ uidRec.pendingChange.gone = gone;
+ uidRec.pendingChange.processState = uidRec.setProcState;
+ }
+
private void maybeUpdateUsageStats(ProcessRecord app) {
if (DEBUG_USAGE_STATS) {
Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
@@ -18463,6 +18631,14 @@
Slog.i(TAG, "updateOomAdj: top=" + TOP_ACT, e);
}
+ // Reset state in all uid records.
+ for (int i=mActiveUids.size()-1; i>=0; i--) {
+ final UidRecord uidRec = mActiveUids.valueAt(i);
+ if (false && DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "Starting update of " + uidRec);
+ uidRec.reset();
+ }
+
mAdjSeq++;
mNewNumServiceProcs = 0;
mNewNumAServiceProcs = 0;
@@ -18610,6 +18786,12 @@
// good to avoid having whatever code was running in them
// left sitting around after no longer needed.
app.kill("isolated not needed", true);
+ } else {
+ // Keeping this process, update its uid.
+ final UidRecord uidRec = app.uidRecord;
+ if (uidRec != null && uidRec.curProcState > app.curProcState) {
+ uidRec.curProcState = app.curProcState;
+ }
}
if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
@@ -18805,6 +18987,18 @@
requestPssAllProcsLocked(now, false, mProcessStats.isMemFactorLowered());
}
+ // Update from any uid changes.
+ for (int i=mActiveUids.size()-1; i>=0; i--) {
+ final UidRecord uidRec = mActiveUids.valueAt(i);
+ if (uidRec.setProcState != uidRec.curProcState) {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "Changes in " + uidRec + ": proc state from " + uidRec.setProcState
+ + " to " + uidRec.curProcState);
+ uidRec.setProcState = uidRec.curProcState;
+ enqueueUidChangeLocked(uidRec, false);
+ }
+ }
+
if (mProcessStats.shouldWriteNowLocked(now)) {
mHandler.post(new Runnable() {
@Override public void run() {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 29e14f8..14759c3 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -62,8 +62,8 @@
final int userId; // user of process.
final String processName; // name of the process
// List of packages running in the process
- final ArrayMap<String, ProcessStats.ProcessStateHolder> pkgList
- = new ArrayMap<String, ProcessStats.ProcessStateHolder>();
+ final ArrayMap<String, ProcessStats.ProcessStateHolder> pkgList = new ArrayMap<>();
+ UidRecord uidRecord; // overall state of process's uid.
ArraySet<String> pkgDeps; // additional packages we have a dependency on
IApplicationThread thread; // the actual proc... may be null only if
// 'persistent' is true (in which case we
@@ -142,24 +142,20 @@
Object adjTarget; // Debugging: target component impacting oom_adj.
Runnable crashHandler; // Optional local handler to be invoked in the process crash.
- // contains HistoryRecord objects
- final ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
+ // all activities running in the process
+ final ArrayList<ActivityRecord> activities = new ArrayList<>();
// all ServiceRecord running in this process
- final ArraySet<ServiceRecord> services = new ArraySet<ServiceRecord>();
+ final ArraySet<ServiceRecord> services = new ArraySet<>();
// services that are currently executing code (need to remain foreground).
- final ArraySet<ServiceRecord> executingServices
- = new ArraySet<ServiceRecord>();
+ final ArraySet<ServiceRecord> executingServices = new ArraySet<>();
// All ConnectionRecord this process holds
- final ArraySet<ConnectionRecord> connections
- = new ArraySet<ConnectionRecord>();
+ final ArraySet<ConnectionRecord> connections = new ArraySet<>();
// all IIntentReceivers that are registered from this process.
- final ArraySet<ReceiverList> receivers = new ArraySet<ReceiverList>();
+ final ArraySet<ReceiverList> receivers = new ArraySet<>();
// class (String) -> ContentProviderRecord
- final ArrayMap<String, ContentProviderRecord> pubProviders
- = new ArrayMap<String, ContentProviderRecord>();
+ final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
// All ContentProviderRecord process is using
- final ArrayList<ContentProviderConnection> conProviders
- = new ArrayList<ContentProviderConnection>();
+ final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();
boolean execServicesFg; // do we need to be executing services in the foreground?
boolean persistent; // always keep this application running?
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
new file mode 100644
index 0000000..b4efbf0
--- /dev/null
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -0,0 +1,62 @@
+/*
+ * 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.server.am;
+
+import android.app.ActivityManager;
+import android.os.UserHandle;
+
+/**
+ * Overall information about a uid that has actively running processes.
+ */
+public final class UidRecord {
+ final int uid;
+ int curProcState;
+ int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+ int numProcs;
+
+ static final class ChangeItem {
+ UidRecord uidRecord;
+ int uid;
+ boolean gone;
+ int processState;
+ }
+
+ ChangeItem pendingChange;
+
+ public UidRecord(int _uid) {
+ uid = _uid;
+ reset();
+ }
+
+ public void reset() {
+ curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("UidRecord{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(' ');
+ UserHandle.formatUid(sb, uid);
+ sb.append(' ');
+ sb.append(ProcessList.makeProcStateString(curProcState));
+ sb.append(" / ");
+ sb.append(numProcs);
+ sb.append(" procs}");
+ return sb.toString();
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 4e83992..fba9258 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -149,7 +149,6 @@
/**
* Force evaluation even if it has succeeded in the past.
* arg1 = UID responsible for requesting this reeval. Will be billed for data.
- * arg2 = Number of evaluation attempts to make. (If 0, make INITIAL_ATTEMPTS attempts.)
*/
public static final int CMD_FORCE_REEVALUATION = BASE + 8;
@@ -183,20 +182,18 @@
private final int mLingerDelayMs;
private int mLingerToken = 0;
- // Negative values disable reevaluation.
- private static final String REEVALUATE_DELAY_PROPERTY = "persist.netmon.reeval_delay";
- // When connecting, attempt to validate 3 times, pausing 5s between them.
- private static final int DEFAULT_REEVALUATE_DELAY_MS = 5000;
- private static final int INITIAL_ATTEMPTS = 3;
- // If a network is not validated, make one attempt every 10 mins to see if it starts working.
- private static final int REEVALUATE_PAUSE_MS = 10*60*1000;
- private static final int PERIODIC_ATTEMPTS = 1;
- // When an application calls reportNetworkConnectivity, only make one attempt.
- private static final int REEVALUATE_ATTEMPTS = 1;
- private final int mReevaluateDelayMs;
+ // Start mReevaluateDelayMs at this value and double.
+ private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
+ private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
+ // Before network has been evaluated this many times, ignore repeated reevaluate requests.
+ private static final int IGNORE_REEVALUATE_ATTEMPTS = 5;
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;
private final Context mContext;
private final Handler mConnectivityServiceHandler;
@@ -211,19 +208,12 @@
// Set if the user explicitly selected "Do not use this network" in captive portal sign-in app.
private boolean mUserDoesNotWant = false;
-
- // How many times we should attempt validation. Only checked in EvaluatingState; must be set
- // before entering EvaluatingState. Note that whatever code causes us to transition to
- // EvaluatingState last decides how many attempts will be made, so if one codepath were to
- // enter EvaluatingState with a specific number of attempts, and then another were to enter it
- // with a different number of attempts, the second number would be used. This is not currently
- // a problem because EvaluatingState is not reentrant.
- private int mMaxAttempts;
+ // Avoids surfacing "Sign in to network" notification.
+ private boolean mDontDisplaySigninNotification = false;
public boolean systemReady = false;
private final State mDefaultState = new DefaultState();
- private final State mOfflineState = new OfflineState();
private final State mValidatedState = new ValidatedState();
private final State mMaybeNotifyState = new MaybeNotifyState();
private final State mEvaluatingState = new EvaluatingState();
@@ -247,7 +237,6 @@
mDefaultRequest = defaultRequest;
addState(mDefaultState);
- addState(mOfflineState, mDefaultState);
addState(mValidatedState, mDefaultState);
addState(mMaybeNotifyState, mDefaultState);
addState(mEvaluatingState, mMaybeNotifyState);
@@ -260,8 +249,6 @@
if (mServer == null) mServer = DEFAULT_SERVER;
mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
- mReevaluateDelayMs = SystemProperties.getInt(REEVALUATE_DELAY_PROPERTY,
- DEFAULT_REEVALUATE_DELAY_MS);
mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1;
@@ -289,7 +276,6 @@
return HANDLED;
case CMD_NETWORK_CONNECTED:
if (DBG) log("Connected");
- mMaxAttempts = INITIAL_ATTEMPTS;
transitionTo(mEvaluatingState);
return HANDLED;
case CMD_NETWORK_DISCONNECTED:
@@ -303,7 +289,6 @@
case CMD_FORCE_REEVALUATION:
if (DBG) log("Forcing reevaluation");
mUidResponsibleForReeval = message.arg1;
- mMaxAttempts = message.arg2 != 0 ? message.arg2 : REEVALUATE_ATTEMPTS;
transitionTo(mEvaluatingState);
return HANDLED;
case CMD_CAPTIVE_PORTAL_APP_FINISHED:
@@ -313,18 +298,23 @@
mCaptivePortalLoggedInResponseToken = String.valueOf(new Random().nextLong());
switch (message.arg1) {
case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_DISMISSED:
- sendMessage(CMD_FORCE_REEVALUATION, 0 /* no UID */,
- 0 /* INITIAL_ATTEMPTS */);
+ sendMessage(CMD_FORCE_REEVALUATION, 0 /* no UID */, 0);
break;
case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS:
+ mDontDisplaySigninNotification = true;
// TODO: Distinguish this from a network that actually validates.
// Displaying the "!" on the system UI icon may still be a good idea.
transitionTo(mValidatedState);
break;
case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_UNWANTED:
+ mDontDisplaySigninNotification = true;
mUserDoesNotWant = true;
+ mConnectivityServiceHandler.sendMessage(obtainMessage(
+ EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, 0,
+ mNetworkAgentInfo));
// TODO: Should teardown network.
- transitionTo(mOfflineState);
+ mUidResponsibleForReeval = 0;
+ transitionTo(mEvaluatingState);
break;
}
return HANDLED;
@@ -334,42 +324,6 @@
}
}
- // Being in the OfflineState State indicates a Network is unwanted or failed validation.
- private class OfflineState extends State {
- @Override
- public void enter() {
- mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
- NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
- if (!mUserDoesNotWant) {
- sendMessageDelayed(CMD_FORCE_REEVALUATION, 0 /* no UID */,
- PERIODIC_ATTEMPTS, REEVALUATE_PAUSE_MS);
- }
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) log(getName() + message.toString());
- switch (message.what) {
- case CMD_FORCE_REEVALUATION:
- // If the user has indicated they explicitly do not want to use this network,
- // don't allow a reevaluation as this will be pointless and could result in
- // the user being annoyed with repeated unwanted notifications.
- return mUserDoesNotWant ? HANDLED : NOT_HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- public void exit() {
- // NOTE: This removes the delayed message posted by enter() but will inadvertently
- // remove any other CMD_FORCE_REEVALUATION in the message queue. At the moment this
- // is harmless. If in the future this becomes problematic a different message could
- // be used.
- removeMessages(CMD_FORCE_REEVALUATION);
- }
- }
-
// Being in the ValidatedState State indicates a Network is:
// - Successfully validated, or
// - Wanted "as is" by the user, or
@@ -426,18 +380,20 @@
}
// Being in the EvaluatingState State indicates the Network is being evaluated for internet
- // connectivity.
+ // connectivity, or that the user has indicated that this network is unwanted.
private class EvaluatingState extends State {
- private int mAttempt;
+ private int mReevaluateDelayMs;
+ private int mAttempts;
@Override
public void enter() {
- mAttempt = 1;
sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
if (mUidResponsibleForReeval != INVALID_UID) {
TrafficStats.setThreadStatsUid(mUidResponsibleForReeval);
mUidResponsibleForReeval = INVALID_UID;
}
+ mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS;
+ mAttempts = 0;
}
@Override
@@ -445,7 +401,7 @@
if (DBG) log(getName() + message.toString());
switch (message.what) {
case CMD_REEVALUATE:
- if (message.arg1 != mReevaluateToken)
+ if (message.arg1 != mReevaluateToken || mUserDoesNotWant)
return HANDLED;
// Don't bother validating networks that don't satisify the default request.
// This includes:
@@ -469,6 +425,7 @@
transitionTo(mValidatedState);
return HANDLED;
}
+ mAttempts++;
// Note: This call to isCaptivePortal() could take up to a minute. Resolving the
// server's IP addresses could hit the DNS timeout, and attempting connections
// to each of the server's several IP addresses (currently one IPv4 and one
@@ -480,16 +437,27 @@
transitionTo(mValidatedState);
} else if (httpResponseCode >= 200 && httpResponseCode <= 399) {
transitionTo(mCaptivePortalState);
- } else if (++mAttempt > mMaxAttempts) {
- transitionTo(mOfflineState);
- } else if (mReevaluateDelayMs >= 0) {
+ } else {
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));
+ // Don't continue to blame UID forever.
+ TrafficStats.clearThreadStatsUid();
+ }
+ mReevaluateDelayMs *= 2;
+ if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
+ mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
+ }
}
return HANDLED;
case CMD_FORCE_REEVALUATION:
- // Ignore duplicate requests.
- return HANDLED;
+ // Before IGNORE_REEVALUATE_ATTEMPTS attempts are made,
+ // ignore any re-evaluation requests. After, restart the
+ // evaluation process via EvaluatingState#enter.
+ return mAttempts < IGNORE_REEVALUATE_ATTEMPTS ? HANDLED : NOT_HANDLED;
default:
return NOT_HANDLED;
}
@@ -533,6 +501,8 @@
public void enter() {
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
+ // Don't annoy user with sign-in notifications.
+ if (mDontDisplaySigninNotification) return;
// Create a CustomIntentReceiver that sends us a
// CMD_LAUNCH_CAPTIVE_PORTAL_APP message when the user
// touches the notification.
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index aeecdf3..e1ec8a6 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -298,13 +298,15 @@
}
/**
- * Set whether the current package has the ability to launch VPNs without user intervention.
+ * Set whether a package has the ability to launch VPNs without user intervention.
*/
- public void setPackageAuthorization(boolean authorized) {
+ public void setPackageAuthorization(String packageName, boolean authorized) {
// Check if the caller is authorized.
enforceControlPermission();
- if (mPackage == null || VpnConfig.LEGACY_VPN.equals(mPackage)) {
+ int uid = getAppUid(packageName, mUserHandle);
+ if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
+ // Authorization for nonexistent packages (or fake ones) can't be updated.
return;
}
@@ -312,10 +314,10 @@
try {
AppOpsManager appOps =
(AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, mOwnerUID, mPackage,
+ appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, uid, packageName,
authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
} catch (Exception e) {
- Log.wtf(TAG, "Failed to set app ops for package " + mPackage, e);
+ Log.wtf(TAG, "Failed to set app ops for package " + packageName + ", uid " + uid, e);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index e516573..517a825 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -17,6 +17,7 @@
package com.android.server.display;
import android.content.res.Resources;
+import android.os.Build;
import com.android.server.LocalServices;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
@@ -50,6 +51,8 @@
private static final String UNIQUE_ID_PREFIX = "local:";
+ private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
+
private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN,
SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI,
@@ -273,8 +276,9 @@
com.android.internal.R.string.display_manager_built_in_display_name);
mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
| DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
- if (res.getBoolean(
- com.android.internal.R.bool.config_mainBuiltInDisplayIsRound)) {
+ if (res.getBoolean(com.android.internal.R.bool.config_mainBuiltInDisplayIsRound)
+ || (Build.HARDWARE.contains("goldfish")
+ && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
}
mInfo.type = Display.TYPE_BUILT_IN;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 24ab3b8..1a79568 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -81,6 +81,7 @@
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.IProcessObserver;
+import android.app.IUidObserver;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.usage.UsageStatsManagerInternal;
@@ -153,10 +154,8 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.AppOpsService;
import com.android.server.DeviceIdleController;
import com.android.server.LocalServices;
-import com.android.server.SystemConfig;
import com.google.android.collect.Lists;
import org.xmlpull.v1.XmlPullParser;
@@ -294,9 +293,8 @@
/** Set of currently active {@link Notification} tags. */
private final ArraySet<String> mActiveNotifs = new ArraySet<String>();
- /** Foreground at both UID and PID granularity. */
+ /** Foreground at UID granularity. */
final SparseIntArray mUidState = new SparseIntArray();
- final SparseArray<SparseIntArray> mUidPidState = new SparseArray<>();
/** The current maximum process state that we are considering to be foreground. */
private int mCurForegroundState = ActivityManager.PROCESS_STATE_TOP;
@@ -411,7 +409,7 @@
updateScreenOn();
try {
- mActivityManager.registerProcessObserver(mProcessObserver);
+ mActivityManager.registerUidObserver(mUidObserver);
mNetworkManager.registerObserver(mAlertObserver);
} catch (RemoteException e) {
// ignored; both services live in system_server
@@ -477,40 +475,16 @@
}
- private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
- @Override
- public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
- }
-
- @Override
- public void onProcessStateChanged(int pid, int uid, int procState) {
+ private IUidObserver mUidObserver = new IUidObserver.Stub() {
+ @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
synchronized (mRulesLock) {
- // because a uid can have multiple pids running inside, we need to
- // remember all pid states and summarize foreground at uid level.
-
- // record foreground for this specific pid
- SparseIntArray pidState = mUidPidState.get(uid);
- if (pidState == null) {
- pidState = new SparseIntArray(2);
- mUidPidState.put(uid, pidState);
- }
- pidState.put(pid, procState);
- computeUidStateLocked(uid);
+ updateUidStateLocked(uid, procState);
}
}
- @Override
- public void onProcessDied(int pid, int uid) {
+ @Override public void onUidGone(int uid) throws RemoteException {
synchronized (mRulesLock) {
- // clear records and recompute, when they exist
- final SparseIntArray pidState = mUidPidState.get(uid);
- if (pidState != null) {
- pidState.delete(pid);
- if (pidState.size() <= 0) {
- mUidPidState.remove(uid);
- }
- computeUidStateLocked(uid);
- }
+ removeUidStateLocked(uid);
}
}
};
@@ -1919,14 +1893,6 @@
fout.print(state);
fout.print(state <= mCurForegroundState ? " (fg)" : " (bg)");
- fout.print(" pids=");
- final int foregroundIndex = mUidPidState.indexOfKey(uid);
- if (foregroundIndex < 0) {
- fout.print("UNKNOWN");
- } else {
- dumpSparseIntArray(fout, mUidPidState.valueAt(foregroundIndex));
- }
-
fout.print(" rules=");
final int rulesIndex = mUidRules.indexOfKey(uid);
if (rulesIndex < 0) {
@@ -1957,36 +1923,38 @@
}
/**
- * Process state of PID changed; recompute state at UID level. If
- * changed, will trigger {@link #updateRulesForUidLocked(int)}.
+ * Process state of UID changed; if needed, will trigger
+ * {@link #updateRulesForUidLocked(int)}.
*/
- void computeUidStateLocked(int uid) {
- final SparseIntArray pidState = mUidPidState.get(uid);
-
- // current pid is dropping foreground; examine other pids
- int uidState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
- if (pidState != null) {
- final int size = pidState.size();
- for (int i = 0; i < size; i++) {
- final int state = pidState.valueAt(i);
- if (state < uidState) {
- uidState = state;
- }
- }
- }
-
+ void updateUidStateLocked(int uid, int uidState) {
final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
if (oldUidState != uidState) {
// state changed, push updated rules
mUidState.put(uid, uidState);
- final boolean oldForeground = oldUidState <= mCurForegroundState;
- final boolean newForeground = uidState <= mCurForegroundState;
- if (oldForeground != newForeground) {
- updateRulesForUidLocked(uid);
+ updateRulesForUidStateChangeLocked(uid, oldUidState, uidState);
+ }
+ }
+
+ void removeUidStateLocked(int uid) {
+ final int index = mUidState.indexOfKey(uid);
+ if (index >= 0) {
+ final int oldUidState = mUidState.valueAt(index);
+ mUidState.removeAt(index);
+ if (oldUidState != ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
+ updateRulesForUidStateChangeLocked(uid, oldUidState,
+ ActivityManager.PROCESS_STATE_CACHED_EMPTY);
}
}
}
+ void updateRulesForUidStateChangeLocked(int uid, int oldUidState, int newUidState) {
+ final boolean oldForeground = oldUidState <= mCurForegroundState;
+ final boolean newForeground = newUidState <= mCurForegroundState;
+ if (oldForeground != newForeground) {
+ updateRulesForUidLocked(uid);
+ }
+ }
+
private void updateScreenOn() {
synchronized (mRulesLock) {
try {
@@ -2381,16 +2349,6 @@
}
}
- private static void dumpSparseIntArray(PrintWriter fout, SparseIntArray value) {
- fout.print("[");
- final int size = value.size();
- for (int i = 0; i < size; i++) {
- fout.print(value.keyAt(i) + "=" + value.valueAt(i));
- if (i < size - 1) fout.print(",");
- }
- fout.print("]");
- }
-
@Override
public void factoryReset(String subscriber) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 509289b..28a786a 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;
@@ -4133,6 +4136,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 +4159,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 +4199,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) {
@@ -6301,7 +6307,6 @@
}
final String path = scanFile.getPath();
- final String codePath = pkg.applicationInfo.getCodePath();
final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
setBundledAppAbisAndRoots(pkg, pkgSetting);
@@ -6327,127 +6332,27 @@
setNativeLibraryPaths(pkg);
} else {
- // TODO: We can probably be smarter about this stuff. For installed apps,
- // we can calculate this information at install time once and for all. For
- // system apps, we can probably assume that this information doesn't change
- // after the first boot scan. As things stand, we do lots of unnecessary work.
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
+ deriveNonSystemPackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */);
+ } else {
+ // Verify the ABIs haven't changed since we last deduced them.
+ String oldPrimaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
+ String oldSecondaryCpuAbi = pkg.applicationInfo.secondaryCpuAbi;
- // Give ourselves some initial paths; we'll come back for another
- // pass once we've determined ABI below.
- setNativeLibraryPaths(pkg);
-
- final boolean isAsec = pkg.isForwardLocked() || isExternal(pkg);
- final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir;
- final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa;
-
- NativeLibraryHelper.Handle handle = null;
- try {
- handle = NativeLibraryHelper.Handle.create(scanFile);
- // TODO(multiArch): This can be null for apps that didn't go through the
- // usual installation process. We can calculate it again, like we
- // do during install time.
- //
- // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally
- // unnecessary.
- final File nativeLibraryRoot = new File(nativeLibraryRootStr);
-
- // Null out the abis so that they can be recalculated.
- pkg.applicationInfo.primaryCpuAbi = null;
- pkg.applicationInfo.secondaryCpuAbi = null;
- if (isMultiArch(pkg.applicationInfo)) {
- // Warn if we've set an abiOverride for multi-lib packages..
- // By definition, we need to copy both 32 and 64 bit libraries for
- // such packages.
- if (pkg.cpuAbiOverride != null
- && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) {
- Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
- }
-
- int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
- int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
- if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
- if (isAsec) {
- abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
- } else {
- abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
- useIsaSpecificSubdirs);
- }
- }
-
- maybeThrowExceptionForMultiArchCopy(
- "Error unpackaging 32 bit native libs for multiarch app.", abi32);
-
- if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
- if (isAsec) {
- abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
- } else {
- abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
- useIsaSpecificSubdirs);
- }
- }
-
- maybeThrowExceptionForMultiArchCopy(
- "Error unpackaging 64 bit native libs for multiarch app.", abi64);
-
- if (abi64 >= 0) {
- pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];
- }
-
- if (abi32 >= 0) {
- final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
- if (abi64 >= 0) {
- pkg.applicationInfo.secondaryCpuAbi = abi;
- } else {
- pkg.applicationInfo.primaryCpuAbi = abi;
- }
- }
- } else {
- String[] abiList = (cpuAbiOverride != null) ?
- new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
-
- // Enable gross and lame hacks for apps that are built with old
- // SDK tools. We must scan their APKs for renderscript bitcode and
- // not launch them if it's present. Don't bother checking on devices
- // that don't have 64 bit support.
- boolean needsRenderScriptOverride = false;
- if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
- NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
- abiList = Build.SUPPORTED_32_BIT_ABIS;
- needsRenderScriptOverride = true;
- }
-
- final int copyRet;
- if (isAsec) {
- copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
- } else {
- copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
- }
-
- if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Error unpackaging native libs for app, errorCode=" + copyRet);
- }
-
- if (copyRet >= 0) {
- pkg.applicationInfo.primaryCpuAbi = abiList[copyRet];
- } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && cpuAbiOverride != null) {
- pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;
- } else if (needsRenderScriptOverride) {
- pkg.applicationInfo.primaryCpuAbi = abiList[0];
- }
+ // TODO: The only purpose of this code is to update the native library paths
+ // based on the final install location. We can simplify this and avoid having
+ // 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 + " ("
+ + oldPrimaryCpuAbi + "-> " + pkg.applicationInfo.primaryCpuAbi);
}
- } catch (IOException ioe) {
- Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
- } finally {
- IoUtils.closeQuietly(handle);
- }
- // Now that we've calculated the ABIs and determined if it's an internal app,
- // we will go ahead and populate the nativeLibraryPath.
- setNativeLibraryPaths(pkg);
+ if (!TextUtils.equals(oldSecondaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi)) {
+ throw new IllegalStateException("unexpected abi change for " + pkg.packageName + " ("
+ + oldSecondaryCpuAbi + "-> " + pkg.applicationInfo.secondaryCpuAbi);
+ }
+ }
if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
final int[] userIds = sUserManager.getUserIds();
@@ -6478,9 +6383,21 @@
Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
}
+ // If there's a mismatch between the abi-override in the package setting
+ // and the abiOverride specified for the install. Warn about this because we
+ // would've already compiled the app without taking the package setting into
+ // account.
+ if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
+ if (cpuAbiOverride == null && pkgSetting.cpuAbiOverrideString != null) {
+ Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
+ " for package: " + pkg.packageName);
+ }
+ }
+
pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
+
// Copy the derived override back to the parsed package, so that we can
// update the package settings accordingly.
pkg.cpuAbiOverride = cpuAbiOverride;
@@ -6974,6 +6891,144 @@
}
/**
+ * Derive the ABI of a non-system package located at {@code scanFile}. This information
+ * is derived purely on the basis of the contents of {@code scanFile} and
+ * {@code cpuAbiOverride}.
+ *
+ * If {@code extractLibs} is true, native libraries are extracted from the app if required.
+ */
+ public void deriveNonSystemPackageAbi(PackageParser.Package pkg, File scanFile,
+ String cpuAbiOverride, boolean extractLibs)
+ throws PackageManagerException {
+ // TODO: We can probably be smarter about this stuff. For installed apps,
+ // we can calculate this information at install time once and for all. For
+ // system apps, we can probably assume that this information doesn't change
+ // after the first boot scan. As things stand, we do lots of unnecessary work.
+
+ // Give ourselves some initial paths; we'll come back for another
+ // pass once we've determined ABI below.
+ setNativeLibraryPaths(pkg);
+
+ // We would never need to extract libs for forward-locked and external packages,
+ // since the container service will do it for us.
+ if (pkg.isForwardLocked() || isExternal(pkg)) {
+ extractLibs = false;
+ }
+
+ final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir;
+ final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa;
+
+ NativeLibraryHelper.Handle handle = null;
+ try {
+ handle = NativeLibraryHelper.Handle.create(scanFile);
+ // TODO(multiArch): This can be null for apps that didn't go through the
+ // usual installation process. We can calculate it again, like we
+ // do during install time.
+ //
+ // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally
+ // unnecessary.
+ final File nativeLibraryRoot = new File(nativeLibraryRootStr);
+
+ // Null out the abis so that they can be recalculated.
+ pkg.applicationInfo.primaryCpuAbi = null;
+ pkg.applicationInfo.secondaryCpuAbi = null;
+ if (isMultiArch(pkg.applicationInfo)) {
+ // Warn if we've set an abiOverride for multi-lib packages..
+ // By definition, we need to copy both 32 and 64 bit libraries for
+ // such packages.
+ if (pkg.cpuAbiOverride != null
+ && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) {
+ Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
+ }
+
+ int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
+ int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
+ if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
+ if (extractLibs) {
+ abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+ nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
+ useIsaSpecificSubdirs);
+ } else {
+ abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
+ }
+ }
+
+ maybeThrowExceptionForMultiArchCopy(
+ "Error unpackaging 32 bit native libs for multiarch app.", abi32);
+
+ if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
+ if (extractLibs) {
+ abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+ nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
+ useIsaSpecificSubdirs);
+ } else {
+ abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
+ }
+ }
+
+ maybeThrowExceptionForMultiArchCopy(
+ "Error unpackaging 64 bit native libs for multiarch app.", abi64);
+
+ if (abi64 >= 0) {
+ pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];
+ }
+
+ if (abi32 >= 0) {
+ final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
+ if (abi64 >= 0) {
+ pkg.applicationInfo.secondaryCpuAbi = abi;
+ } else {
+ pkg.applicationInfo.primaryCpuAbi = abi;
+ }
+ }
+ } else {
+ String[] abiList = (cpuAbiOverride != null) ?
+ new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
+
+ // Enable gross and lame hacks for apps that are built with old
+ // SDK tools. We must scan their APKs for renderscript bitcode and
+ // not launch them if it's present. Don't bother checking on devices
+ // that don't have 64 bit support.
+ boolean needsRenderScriptOverride = false;
+ if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
+ NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
+ abiList = Build.SUPPORTED_32_BIT_ABIS;
+ needsRenderScriptOverride = true;
+ }
+
+ final int copyRet;
+ if (extractLibs) {
+ copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+ nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
+ } else {
+ copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
+ }
+
+ if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Error unpackaging native libs for app, errorCode=" + copyRet);
+ }
+
+ if (copyRet >= 0) {
+ pkg.applicationInfo.primaryCpuAbi = abiList[copyRet];
+ } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && cpuAbiOverride != null) {
+ pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;
+ } else if (needsRenderScriptOverride) {
+ pkg.applicationInfo.primaryCpuAbi = abiList[0];
+ }
+ }
+ } catch (IOException ioe) {
+ Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
+ } finally {
+ IoUtils.closeQuietly(handle);
+ }
+
+ // Now that we've calculated the ABIs and determined if it's an internal app,
+ // we will go ahead and populate the nativeLibraryPath.
+ setNativeLibraryPaths(pkg);
+ }
+
+ /**
* Adjusts ABIs for a set of packages belonging to a shared user so that they all match.
* i.e, so that all packages can be run inside a single process if required.
*
@@ -7689,7 +7744,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)) {
@@ -7749,6 +7804,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) {
@@ -11566,6 +11643,16 @@
} else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
// Enable SCAN_NO_DEX flag to skip dexopt at a later stage
scanFlags |= SCAN_NO_DEX;
+
+ try {
+ deriveNonSystemPackageAbi(pkg, new File(pkg.codePath), args.abiOverride,
+ true /* extract libs */);
+ } catch (PackageManagerException pme) {
+ Slog.e(TAG, "Error deriving application ABI", pme);
+ res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error ");
+ return;
+ }
+
// Run dexopt before old package gets removed, to minimize time when app is unavailable
int result = mPackageDexOptimizer
.performDexOpt(pkg, null /* instruction sets */, true /* forceDex */,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 40c8ca3..15d1535a 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -580,6 +580,28 @@
* Check if we've hit the limit of how many users can be created.
*/
private boolean isUserLimitReachedLocked() {
+ return getAliveUsersExcludingGuestsCountLocked() >= UserManager.getMaxSupportedUsers();
+ }
+
+ @Override
+ public boolean canAddMoreManagedProfiles() {
+ checkManageUsersPermission("check if more managed profiles can be added.");
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ return false;
+ }
+ synchronized(mPackagesLock) {
+ // Limit number of managed profiles that can be created
+ if (numberOfUsersOfTypeLocked(UserInfo.FLAG_MANAGED_PROFILE, true)
+ >= MAX_MANAGED_PROFILES) {
+ return false;
+ }
+ int usersCount = getAliveUsersExcludingGuestsCountLocked();
+ // We allow creating a managed profile in the special case where there is only one user.
+ return usersCount == 1 || usersCount < UserManager.getMaxSupportedUsers();
+ }
+ }
+
+ private int getAliveUsersExcludingGuestsCountLocked() {
int aliveUserCount = 0;
final int totalUserCount = mUsers.size();
// Skip over users being removed
@@ -590,7 +612,7 @@
aliveUserCount++;
}
}
- return aliveUserCount >= UserManager.getMaxSupportedUsers();
+ return aliveUserCount;
}
/**
@@ -1176,7 +1198,11 @@
Log.w(LOG_TAG, "Cannot add user. DISALLOW_ADD_USER is enabled.");
return null;
}
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ return null;
+ }
final boolean isGuest = (flags & UserInfo.FLAG_GUEST) != 0;
+ final boolean isManagedProfile = (flags & UserInfo.FLAG_MANAGED_PROFILE) != 0;
final long ident = Binder.clearCallingIdentity();
UserInfo userInfo = null;
try {
@@ -1187,21 +1213,18 @@
parent = getUserInfoLocked(parentId);
if (parent == null) return null;
}
- // If we're not adding a guest user and the limit has been reached,
- // cannot add a user.
- if (!isGuest && isUserLimitReachedLocked()) {
+ if (isManagedProfile && !canAddMoreManagedProfiles()) {
+ return null;
+ }
+ if (!isGuest && !isManagedProfile && isUserLimitReachedLocked()) {
+ // If we're not adding a guest user or a managed profile and the limit has
+ // been reached, cannot add a user.
return null;
}
// If we're adding a guest and there already exists one, bail.
if (isGuest && findCurrentGuestUserLocked() != null) {
return null;
}
- // Limit number of managed profiles that can be created
- if ((flags & UserInfo.FLAG_MANAGED_PROFILE) != 0
- && numberOfUsersOfTypeLocked(UserInfo.FLAG_MANAGED_PROFILE, true)
- >= MAX_MANAGED_PROFILES) {
- return null;
- }
int userId = getNextAvailableIdLocked();
userInfo = new UserInfo(userId, name, null, flags);
File userPath = new File(mBaseUserPath, Integer.toString(userId));
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 185ef03..a4e9c68 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1220,9 +1220,7 @@
};
private boolean isRoundWindow() {
- return mContext.getResources().getBoolean(com.android.internal.R.bool.config_windowIsRound)
- || (Build.HARDWARE.contains("goldfish")
- && SystemProperties.getBoolean(ViewRootImpl.PROPERTY_EMULATOR_CIRCULAR, false));
+ return mContext.getResources().getConfiguration().isScreenRound();
}
/** {@inheritDoc} */
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 74df0a0..5aea746 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.power;
+import android.app.ActivityManager;
+import android.util.SparseIntArray;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
@@ -436,6 +438,8 @@
// Set of app ids that we will always respect the wake locks for.
int[] mDeviceIdleWhitelist = new int[0];
+ private final SparseIntArray mUidState = new SparseIntArray();
+
// True if theater mode is enabled
private boolean mTheaterModeEnabled;
@@ -2316,6 +2320,24 @@
}
}
+ void updateUidProcStateInternal(int uid, int procState) {
+ synchronized (mLock) {
+ mUidState.put(uid, procState);
+ if (mDeviceIdleMode) {
+ updateWakeLockDisabledStatesLocked();
+ }
+ }
+ }
+
+ void uidGoneInternal(int uid) {
+ synchronized (mLock) {
+ mUidState.delete(uid);
+ if (mDeviceIdleMode) {
+ updateWakeLockDisabledStatesLocked();
+ }
+ }
+ }
+
private void updateWakeLockDisabledStatesLocked() {
boolean changed = false;
final int numWakeLocks = mWakeLocks.size();
@@ -2349,7 +2371,10 @@
// If we are in idle mode, we will ignore all partial wake locks that are
// for application uids that are not whitelisted.
if (appid >= Process.FIRST_APPLICATION_UID &&
- Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0) {
+ Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0 &&
+ mUidState.get(wakeLock.mOwnerUid,
+ ActivityManager.PROCESS_STATE_CACHED_EMPTY)
+ > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
disabled = true;
}
}
@@ -2650,6 +2675,13 @@
pw.println("Screen dim duration: " + screenDimDuration + " ms");
pw.println();
+ pw.println("UID states:");
+ for (int i=0; i<mUidState.size(); i++) {
+ pw.print(" UID "); UserHandle.formatUid(pw, mUidState.keyAt(i));
+ pw.print(": "); pw.println(mUidState.valueAt(i));
+ }
+
+ pw.println();
pw.println("Wake Locks: size=" + mWakeLocks.size());
for (WakeLock wl : mWakeLocks) {
pw.println(" " + wl);
@@ -3451,5 +3483,15 @@
public void setDeviceIdleWhitelist(int[] appids) {
setDeviceIdleWhitelistInternal(appids);
}
+
+ @Override
+ public void updateUidProcState(int uid, int procState) {
+ updateUidProcStateInternal(uid, procState);
+ }
+
+ @Override
+ public void uidGone(int uid) {
+ uidGoneInternal(uid);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 91ce739..482ae24 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -432,8 +432,7 @@
mDrawBorderInset = (int) mBorderWidth / 2;
mWindow = new ViewportWindow(mContext);
- if (mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_windowIsRound)) {
+ if (mContext.getResources().getConfiguration().isScreenRound()) {
mCircularPath = new Path();
mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
final int centerXY = mTempPoint.x / 2;
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 0357de2..60bbc48 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -325,8 +325,7 @@
mListeners.add(listener);
}
- public void notifyAppTransitionFinishedLocked(AppWindowAnimator animator) {
- IBinder token = animator != null ? animator.mAppToken.token : null;
+ public void notifyAppTransitionFinishedLocked(IBinder token) {
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onAppTransitionFinishedLocked(token);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 3feec82..2e89385 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -345,7 +345,7 @@
for (int i = 0; i < numAllAppWinAnimators; i++) {
mAllAppWinAnimators.get(i).finishExit();
}
- mService.mAppTransition.notifyAppTransitionFinishedLocked(this);
+ mService.mAppTransition.notifyAppTransitionFinishedLocked(mAppToken.token);
return false;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e43861c..d956d76 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -252,11 +252,6 @@
static final int LAYER_OFFSET_DIM = 1;
/**
- * Blur surface layer is immediately below dim layer.
- */
- static final int LAYER_OFFSET_BLUR = 2;
-
- /**
* FocusedStackFrame layer is immediately above focused window.
*/
static final int LAYER_OFFSET_FOCUSED_STACK = 1;
@@ -266,27 +261,12 @@
* the thumbnail (or in other words as far as possible above the window
* below it).
*/
- static final int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER-1;
-
- /**
- * Layer at which to put the rotation freeze snapshot.
- */
- static final int FREEZE_LAYER = (TYPE_LAYER_MULTIPLIER * 200) + 1;
-
- /**
- * Layer at which to put the mask for emulated screen sizes.
- */
- static final int MASK_LAYER = TYPE_LAYER_MULTIPLIER * 200;
+ static final int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER - 1;
/** The maximum length we will accept for a loaded animation duration:
* this is 10 seconds.
*/
- static final int MAX_ANIMATION_DURATION = 10*1000;
-
- /** Amount of time (in milliseconds) to animate the fade-in-out transition for
- * compatible windows.
- */
- static final int DEFAULT_FADE_IN_OUT_DURATION = 400;
+ static final int MAX_ANIMATION_DURATION = 10 * 1000;
/** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000;
@@ -381,48 +361,43 @@
/**
* All currently active sessions with clients.
*/
- final ArraySet<Session> mSessions = new ArraySet<Session>();
+ final ArraySet<Session> mSessions = new ArraySet<>();
/**
* Mapping from an IWindow IBinder to the server's Window object.
* This is also used as the lock for all of our state.
* NOTE: Never call into methods that lock ActivityManagerService while holding this object.
*/
- final HashMap<IBinder, WindowState> mWindowMap = new HashMap<IBinder, WindowState>();
+ final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();
/**
* Mapping from a token IBinder to a WindowToken object.
*/
- final HashMap<IBinder, WindowToken> mTokenMap = new HashMap<IBinder, WindowToken>();
+ final HashMap<IBinder, WindowToken> mTokenMap = new HashMap<>();
/**
* List of window tokens that have finished starting their application,
* and now need to have the policy remove their windows.
*/
- final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<AppWindowToken>();
+ final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();
/**
* Fake windows added to the window manager. Note: ordered from top to
* bottom, opposite of mWindows.
*/
- final ArrayList<FakeWindowImpl> mFakeWindows = new ArrayList<FakeWindowImpl>();
+ final ArrayList<FakeWindowImpl> mFakeWindows = new ArrayList<>();
/**
* Windows that are being resized. Used so we can tell the client about
* the resize after closing the transaction in which we resized the
* underlying surface.
*/
- final ArrayList<WindowState> mResizingWindows = new ArrayList<WindowState>();
+ final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
/**
* Windows whose animations have ended and now must be removed.
*/
- final ArrayList<WindowState> mPendingRemove = new ArrayList<WindowState>();
-
- /**
- * Stacks whose animations have ended and whose tasks, apps, selves may now be removed.
- */
- final ArraySet<TaskStack> mPendingStacksRemove = new ArraySet<TaskStack>();
+ final ArrayList<WindowState> mPendingRemove = new ArrayList<>();
/**
* Used when processing mPendingRemove to avoid working on the original array.
@@ -432,13 +407,13 @@
/**
* Windows whose surface should be destroyed.
*/
- final ArrayList<WindowState> mDestroySurface = new ArrayList<WindowState>();
+ final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
/**
* Windows that have lost input focus and are waiting for the new
* focus window to be displayed before they are told about this.
*/
- ArrayList<WindowState> mLosingFocus = new ArrayList<WindowState>();
+ ArrayList<WindowState> mLosingFocus = new ArrayList<>();
/**
* This is set when we have run out of memory, and will either be an empty
@@ -449,7 +424,7 @@
/**
* Windows that clients are waiting to have drawn.
*/
- ArrayList<WindowState> mWaitingForDrawn = new ArrayList<WindowState>();
+ ArrayList<WindowState> mWaitingForDrawn = new ArrayList<>();
/**
* And the callback to make when they've all been drawn.
*/
@@ -466,7 +441,7 @@
* This array is essentially a cache for all userId for
* {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled}
*/
- SparseArray<Boolean> mScreenCaptureDisabled = new SparseArray<Boolean>();
+ SparseArray<Boolean> mScreenCaptureDisabled = new SparseArray<>();
IInputMethodManager mInputMethodManager;
@@ -840,8 +815,7 @@
boolean mInTouchMode;
private ViewServer mViewServer;
- private final ArrayList<WindowChangeListener> mWindowChangeListeners =
- new ArrayList<WindowChangeListener>();
+ private final ArrayList<WindowChangeListener> mWindowChangeListeners = new ArrayList<>();
private boolean mWindowsChanged = false;
public interface WindowChangeListener {
@@ -859,6 +833,10 @@
// For example, when this flag is true, there will be no wallpaper service.
final boolean mOnlyCore;
+ // List of clients without a transtiton animation that we notify once we are done transitioning
+ // since they won't be notified through the app window animator.
+ private final List<IBinder> mNoAnimationNotifyOnTransitionFinished = new ArrayList<>();
+
/** Listener to notify activity manager about app transitions. */
private final WindowManagerInternal.AppTransitionListener mActivityManagerAppTransitionNotifier
= new WindowManagerInternal.AppTransitionListener() {
@@ -3209,10 +3187,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) {
@@ -5923,8 +5904,7 @@
public void updateCircularDisplayMaskIfNeeded() {
// we're fullscreen and not hosted in an ActivityView
- if (mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_windowIsRound)
+ if (mContext.getResources().getConfiguration().isScreenRound()
&& mContext.getResources().getBoolean(
com.android.internal.R.bool.config_windowShowCircularMask)) {
// Device configuration calls for a circular display mask, but we only enable the mask
@@ -9226,6 +9206,7 @@
transit = AppTransition.TRANSIT_UNSET;
}
mSkipAppTransitionAnimation = false;
+ mNoAnimationNotifyOnTransitionFinished.clear();
mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
@@ -9395,7 +9376,13 @@
appAnimator.animation = null;
}
wtoken.inPendingTransaction = false;
- setTokenVisibilityLocked(wtoken, animLp, true, transit, false, voiceInteraction);
+ if (!setTokenVisibilityLocked(
+ wtoken, animLp, true, transit, false, voiceInteraction)){
+ // This token isn't going to be animating. Add it to the list of tokens to
+ // be notified of app transition complete since the notification will not be
+ // sent be the app window animator.
+ mNoAnimationNotifyOnTransitionFinished.add(wtoken.token);
+ }
wtoken.updateReportedVisibilityLocked();
wtoken.waitingToShow = false;
@@ -9562,6 +9549,12 @@
mAppTransition.setIdle();
+ for (int i = mNoAnimationNotifyOnTransitionFinished.size() - 1; i >= 0; i--) {
+ final IBinder token = mNoAnimationNotifyOnTransitionFinished.get(i);
+ mAppTransition.notifyAppTransitionFinishedLocked(token);
+ }
+ mNoAnimationNotifyOnTransitionFinished.clear();
+
if (mDeferredHideWallpaper != null) {
hideWallpapersLocked(mDeferredHideWallpaper);
mDeferredHideWallpaper = null;
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/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 27c97d0..638783d 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -50,7 +50,7 @@
*/
public final class UsbAlsaManager {
private static final String TAG = UsbAlsaManager.class.getSimpleName();
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private static final String ALSA_DIRECTORY = "/dev/snd/";
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 42eb6c3..03abfba 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -35,6 +35,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.IVoiceInteractionSessionService;
import android.service.voice.VoiceInteractionService;
@@ -177,6 +178,8 @@
public boolean showLocked(Bundle args, int flags,
IVoiceInteractionSessionShowCallback showCallback) {
+ // For now we never allow screenshots.
+ flags &= ~VoiceInteractionService.START_WITH_SCREENSHOT;
if (mBound) {
if (!mFullyBound) {
mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
@@ -190,7 +193,8 @@
mHaveAssistData = false;
if ((flags&VoiceInteractionService.START_WITH_ASSIST) != 0) {
if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_STRUCTURE, mCallingUid,
- mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED) {
+ mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED
+ && isStructureEnabled()) {
try {
mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL,
mAssistReceiver);
@@ -455,6 +459,11 @@
}
}
+ private boolean isStructureEnabled() {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, mUser) != 0;
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("mToken="); pw.println(mToken);
pw.print(prefix); pw.print("mShown="); pw.println(mShown);
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/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index 572fdc9..771cf57 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -444,7 +444,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 +474,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 +494,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/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;