Merge "Fix unsafety in SkiaShader storage, and texture unit accounting" into mnc-dev
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 6302521..fb81fd1 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -240,6 +240,8 @@
as.close();
throw new IOException("bt socket acept failed");
}
+
+ as.mPfd = new ParcelFileDescriptor(fds[0]);
as.mSocket = new LocalSocket(fds[0]);
as.mSocketIS = as.mSocket.getInputStream();
as.mSocketOS = as.mSocket.getOutputStream();
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 103ee29..ceb610a 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -502,9 +502,6 @@
void addOnPermissionsChangeListener(in IOnPermissionsChangeListener listener);
void removeOnPermissionsChangeListener(in IOnPermissionsChangeListener listener);
-
- int getMountExternalMode(int uid);
-
void grantDefaultPermissionsToEnabledCarrierApps(in String[] packageNames, int userId);
boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId);
diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java
index 96000dd..4dbac05 100644
--- a/core/java/android/content/pm/IntentFilterVerificationInfo.java
+++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java
@@ -26,7 +26,9 @@
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+
import com.android.internal.util.XmlUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -181,14 +183,28 @@
return getStatusStringFromValue(mMainStatus);
}
- public static String getStatusStringFromValue(int val) {
- switch (val) {
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK : return "ask";
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS : return "always";
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER : return "never";
+ public static String getStatusStringFromValue(long val) {
+ StringBuilder sb = new StringBuilder();
+ switch ((int)(val >> 32)) {
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
+ sb.append("always : ");
+ sb.append(Long.toHexString(val & 0x00000000FFFFFFFF));
+ break;
+
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+ sb.append("ask");
+ break;
+
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
+ sb.append("never");
+ break;
+
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
default:
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED : return "undefined";
+ sb.append("undefined");
+ break;
}
+ return sb.toString();
}
@Override
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 92b8055..9b28401 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -38,6 +38,7 @@
public ArraySet<String> enabledComponents;
public int domainVerificationStatus;
+ public int appLinkGeneration;
public PackageUserState() {
installed = true;
@@ -60,5 +61,6 @@
? new ArraySet<>(o.enabledComponents) : null;
blockUninstall = o.blockUninstall;
domainVerificationStatus = o.domainVerificationStatus;
+ appLinkGeneration = o.appLinkGeneration;
}
}
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 579634f..19921b5 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -603,7 +603,7 @@
* @hide only for resource preloading
*/
public ConstantState<ColorStateList> getConstantState() {
- if (mFactory != null) {
+ if (mFactory == null) {
mFactory = new ColorStateListFactory(this);
}
return mFactory;
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 9a99a46..731903c 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1918,6 +1918,7 @@
other.mResId = mResId;
other.mForce = mForce;
other.mCount = mCount;
+ other.mHashCode = mHashCode;
return other;
}
}
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index e7deae8..30cdfd3 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -123,7 +123,10 @@
SensorEventQueue queue = mSensorListeners.get(listener);
if (queue == null) {
Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
- queue = new SensorEventQueue(listener, looper, this);
+ final String fullClassName = listener.getClass().getEnclosingClass() != null ?
+ listener.getClass().getEnclosingClass().getName() :
+ listener.getClass().getName();
+ queue = new SensorEventQueue(listener, looper, this, fullClassName);
if (!queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs)) {
queue.dispose();
return false;
@@ -166,12 +169,17 @@
protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
+ if (listener == null) throw new IllegalArgumentException("listener cannot be null");
+
if (sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) return false;
synchronized (mTriggerListeners) {
TriggerEventQueue queue = mTriggerListeners.get(listener);
if (queue == null) {
- queue = new TriggerEventQueue(listener, mMainLooper, this);
+ final String fullClassName = listener.getClass().getEnclosingClass() != null ?
+ listener.getClass().getEnclosingClass().getName() :
+ listener.getClass().getName();
+ queue = new TriggerEventQueue(listener, mMainLooper, this, fullClassName);
if (!queue.addSensor(sensor, 0, 0)) {
queue.dispose();
return false;
@@ -234,7 +242,8 @@
}
// Initialize a client for data_injection.
if (mInjectEventQueue == null) {
- mInjectEventQueue = new InjectEventQueue(mMainLooper, this);
+ mInjectEventQueue = new InjectEventQueue(mMainLooper, this,
+ mContext.getPackageName());
}
} else {
// If data injection is being disabled clean up the native resources.
@@ -296,10 +305,11 @@
protected static final int OPERATING_MODE_NORMAL = 0;
protected static final int OPERATING_MODE_DATA_INJECTION = 1;
- BaseEventQueue(Looper looper, SystemSensorManager manager, int mode) {
+ BaseEventQueue(Looper looper, SystemSensorManager manager, int mode, String packageName) {
+ if (packageName == null) packageName = "";
nSensorEventQueue = nativeInitBaseEventQueue(manager.mNativeInstance,
new WeakReference<>(this), looper.getQueue(), mScratch,
- manager.mContext.getPackageName(), mode, manager.mContext.getOpPackageName());
+ packageName, mode, manager.mContext.getOpPackageName());
mCloseGuard.open("dispose");
mManager = manager;
}
@@ -419,8 +429,8 @@
private final SparseArray<SensorEvent> mSensorsEvents = new SparseArray<SensorEvent>();
public SensorEventQueue(SensorEventListener listener, Looper looper,
- SystemSensorManager manager) {
- super(looper, manager, OPERATING_MODE_NORMAL);
+ SystemSensorManager manager, String packageName) {
+ super(looper, manager, OPERATING_MODE_NORMAL, packageName);
mListener = listener;
}
@@ -486,8 +496,8 @@
private final SparseArray<TriggerEvent> mTriggerEvents = new SparseArray<TriggerEvent>();
public TriggerEventQueue(TriggerEventListener listener, Looper looper,
- SystemSensorManager manager) {
- super(looper, manager, OPERATING_MODE_NORMAL);
+ SystemSensorManager manager, String packageName) {
+ super(looper, manager, OPERATING_MODE_NORMAL, packageName);
mListener = listener;
}
@@ -540,8 +550,8 @@
}
final class InjectEventQueue extends BaseEventQueue {
- public InjectEventQueue(Looper looper, SystemSensorManager manager) {
- super(looper, manager, OPERATING_MODE_DATA_INJECTION);
+ public InjectEventQueue(Looper looper, SystemSensorManager manager, String packageName) {
+ super(looper, manager, OPERATING_MODE_DATA_INJECTION, packageName);
}
int injectSensorData(int handle, float[] values,int accuracy, long timestamp) {
diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java
index 8bc2876..b039fc7 100644
--- a/core/java/android/inputmethodservice/ExtractEditText.java
+++ b/core/java/android/inputmethodservice/ExtractEditText.java
@@ -29,7 +29,7 @@
public class ExtractEditText extends EditText {
private InputMethodService mIME;
private int mSettingExtractedText;
-
+
public ExtractEditText(Context context) {
super(context, null);
}
@@ -45,11 +45,11 @@
public ExtractEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
-
+
void setIME(InputMethodService ime) {
mIME = ime;
}
-
+
/**
* Start making changes that will not be reported to the client. That
* is, {@link #onSelectionChanged(int, int)} will not result in sending
@@ -58,7 +58,7 @@
public void startInternalChanges() {
mSettingExtractedText += 1;
}
-
+
/**
* Finish making changes that will not be reported to the client. That
* is, {@link #onSelectionChanged(int, int)} will not result in sending
@@ -67,7 +67,7 @@
public void finishInternalChanges() {
mSettingExtractedText -= 1;
}
-
+
/**
* Implement just to keep track of when we are setting text from the
* client (vs. seeing changes in ourself from the user).
@@ -80,7 +80,7 @@
mSettingExtractedText--;
}
}
-
+
/**
* Report to the underlying text editor about selection changes.
*/
@@ -89,7 +89,7 @@
mIME.onExtractedSelectionChanged(selStart, selEnd);
}
}
-
+
/**
* Redirect clicks to the IME for handling there. First allows any
* on click handler to run, though.
@@ -101,17 +101,18 @@
}
return false;
}
-
+
@Override public boolean onTextContextMenuItem(int id) {
- if (mIME != null && mIME.onExtractTextContextMenuItem(id)) {
+ // Select all shouldn't be handled by the original edit text, but by the extracted one.
+ if (id != android.R.id.selectAll && mIME != null && mIME.onExtractTextContextMenuItem(id)) {
// Mode was started on Extracted, needs to be stopped here.
- // Cut and paste will change the text, which stops selection mode.
- if (id == android.R.id.copy) stopTextActionMode();
+ // Cut will change the text, which stops selection mode.
+ if (id == android.R.id.copy || id == android.R.id.paste) stopTextActionMode();
return true;
}
return super.onTextContextMenuItem(id);
}
-
+
/**
* We are always considered to be an input method target.
*/
@@ -119,14 +120,14 @@
public boolean isInputMethodTarget() {
return true;
}
-
+
/**
* Return true if the edit text is currently showing a scroll bar.
*/
public boolean hasVerticalScrollBar() {
return computeVerticalScrollRange() > computeVerticalScrollExtent();
}
-
+
/**
* Pretend like the window this view is in always has focus, so its
* highlight and cursor will be displayed.
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 84a879c..c3b098b 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -758,13 +758,15 @@
return _result;
}
- public StorageVolume[] getVolumeList(int userId) throws RemoteException {
+ public StorageVolume[] getVolumeList(int uid, String packageName)
+ throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
StorageVolume[] _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
- _data.writeInt(userId);
+ _data.writeInt(uid);
+ _data.writeString(packageName);
mRemote.transact(Stub.TRANSACTION_getVolumeList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArray(StorageVolume.CREATOR);
@@ -1177,21 +1179,6 @@
_data.recycle();
}
}
-
- @Override
- public void remountUid(int uid) throws RemoteException {
- Parcel _data = Parcel.obtain();
- Parcel _reply = Parcel.obtain();
- try {
- _data.writeInterfaceToken(DESCRIPTOR);
- _data.writeInt(uid);
- mRemote.transact(Stub.TRANSACTION_remountUid, _data, _reply, 0);
- _reply.readException();
- } finally {
- _reply.recycle();
- _data.recycle();
- }
- }
}
private static final String DESCRIPTOR = "IMountService";
@@ -1307,8 +1294,6 @@
static final int TRANSACTION_benchmark = IBinder.FIRST_CALL_TRANSACTION + 59;
static final int TRANSACTION_setDebugFlags = IBinder.FIRST_CALL_TRANSACTION + 60;
- static final int TRANSACTION_remountUid = IBinder.FIRST_CALL_TRANSACTION + 61;
-
/**
* Cast an IBinder object into an IMountService interface, generating a
* proxy if needed.
@@ -1622,8 +1607,9 @@
}
case TRANSACTION_getVolumeList: {
data.enforceInterface(DESCRIPTOR);
- int userId = data.readInt();
- StorageVolume[] result = getVolumeList(userId);
+ int uid = data.readInt();
+ String packageName = data.readString();
+ StorageVolume[] result = getVolumeList(uid, packageName);
reply.writeNoException();
reply.writeTypedArray(result, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
return true;
@@ -1862,13 +1848,6 @@
reply.writeNoException();
return true;
}
- case TRANSACTION_remountUid: {
- data.enforceInterface(DESCRIPTOR);
- int uid = data.readInt();
- remountUid(uid);
- reply.writeNoException();
- return true;
- }
}
return super.onTransact(code, data, reply, flags);
}
@@ -2080,11 +2059,11 @@
/**
* Returns list of all mountable volumes.
*/
- public StorageVolume[] getVolumeList(int userId) throws RemoteException;
+ public StorageVolume[] getVolumeList(int uid, String packageName) throws RemoteException;
/**
* Gets the path on the filesystem for the ASEC container itself.
- *
+ *
* @param cid ASEC container ID
* @return path to filesystem or {@code null} if it's not found
* @throws RemoteException
@@ -2178,6 +2157,4 @@
public String getPrimaryStorageUuid() throws RemoteException;
public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
throws RemoteException;
-
- public void remountUid(int uid) throws RemoteException;
}
diff --git a/core/java/android/os/storage/MountServiceInternal.java b/core/java/android/os/storage/MountServiceInternal.java
new file mode 100644
index 0000000..17aaef9
--- /dev/null
+++ b/core/java/android/os/storage/MountServiceInternal.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.storage;
+
+/**
+ * Mount service local interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class MountServiceInternal {
+
+ /**
+ * Policy that influences how external storage is mounted and reported.
+ */
+ public interface ExternalStorageMountPolicy {
+ /**
+ * Gets the external storage mount mode for the given uid.
+ *
+ * @param uid The UID for which to determine mount mode.
+ * @param packageName The package in the UID for making the call.
+ * @return The mount mode.
+ *
+ * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_NONE
+ * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_DEFAULT
+ * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_READ
+ * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_WRITE
+ */
+ public int getMountMode(int uid, String packageName);
+
+ /**
+ * Gets whether external storage should be reported to the given UID.
+ *
+ * @param uid The UID for which to determine whether it has external storage.
+ * @param packageName The package in the UID for making the call.
+ * @return Weather to report external storage.
+ * @return True to report the state of external storage, false to
+ * report it as unmounted.
+ */
+ public boolean hasExternalStorage(int uid, String packageName);
+ }
+
+ /**
+ * Adds a policy for determining how external storage is mounted and reported.
+ * The mount mode is the most conservative result from querying all registered
+ * policies. Similarly, the reported state is the most conservative result from
+ * querying all registered policies.
+ *
+ * @param policy The policy to add.
+ */
+ public abstract void addExternalStoragePolicy(ExternalStorageMountPolicy policy);
+
+ /**
+ * Notify the mount service that the mount policy for a UID changed.
+ * @param uid The UID for which policy changed.
+ * @param packageName The package in the UID for making the call.
+ */
+ public abstract void onExternalStoragePolicyChanged(int uid, String packageName);
+
+ /**
+ * Gets the mount mode to use for a given UID as determined by consultin all
+ * policies.
+ *
+ * @param uid The UID for which to get mount mode.
+ * @param packageName The package in the UID for making the call.
+ * @return The mount mode.
+ */
+ public abstract int getExternalStorageMountMode(int uid, String packageName);
+}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index f03e04e..140f317 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityThread;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.IPackageMoveObserver;
@@ -857,7 +858,9 @@
final IMountService mountService = IMountService.Stub.asInterface(
ServiceManager.getService("mount"));
try {
- return mountService.getVolumeList(userId);
+ final String packageName = ActivityThread.currentOpPackageName();
+ final int uid = ActivityThread.getPackageManager().getPackageUid(packageName, userId);
+ return mountService.getVolumeList(uid, packageName);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -894,15 +897,6 @@
}
/** {@hide} */
- public void remountUid(int uid) {
- try {
- mMountService.remountUid(uid);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }
-
- /** {@hide} */
private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 32f7bc9..8d603a1 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -298,14 +298,14 @@
}
}
- public StorageVolume buildStorageVolume(Context context, int userId) {
+ public StorageVolume buildStorageVolume(Context context, int userId, boolean reportUnmounted) {
final StorageManager storage = context.getSystemService(StorageManager.class);
final boolean removable;
final boolean emulated;
final boolean allowMassStorage = false;
- final String envState = getEnvironmentForState(state);
-
+ final String envState = reportUnmounted
+ ? Environment.MEDIA_UNMOUNTED : getEnvironmentForState(state);
File userPath = getPathForUser(userId);
if (userPath == null) {
userPath = new File("/dev/null");
diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java
index 4862f01..39f66a5 100644
--- a/core/java/android/util/LocalLog.java
+++ b/core/java/android/util/LocalLog.java
@@ -55,6 +55,12 @@
}
}
+ public synchronized void reverseDump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ for (int i = mLog.size() - 1; i >= 0; i--) {
+ pw.println(mLog.get(i));
+ }
+ }
+
public static class ReadOnlyLocalLog {
private final LocalLog mLog;
ReadOnlyLocalLog(LocalLog log) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 42ac599..131ba46 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6347,17 +6347,28 @@
if (text.text != null) {
if (content == null) {
setText(text.text, TextView.BufferType.EDITABLE);
- } else if (text.partialStartOffset < 0) {
- removeParcelableSpans(content, 0, content.length());
- content.replace(0, content.length(), text.text);
} else {
- final int N = content.length();
- int start = text.partialStartOffset;
- if (start > N) start = N;
- int end = text.partialEndOffset;
- if (end > N) end = N;
+ int start = 0;
+ int end = content.length();
+
+ if (text.partialStartOffset >= 0) {
+ final int N = content.length();
+ start = text.partialStartOffset;
+ if (start > N) start = N;
+ end = text.partialEndOffset;
+ if (end > N) end = N;
+ }
+
removeParcelableSpans(content, start, end);
- content.replace(start, end, text.text);
+ if (TextUtils.equals(content.subSequence(start, end), text.text)) {
+ if (text.text instanceof Spanned) {
+ // OK to copy spans only.
+ TextUtils.copySpansFrom((Spanned) text.text, start, end,
+ Object.class, content, start);
+ }
+ } else {
+ content.replace(start, end, text.text);
+ }
}
}
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index ef2fef0..41628d0 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -41,13 +41,13 @@
private final ActionMode.Callback2 mCallback;
private final MenuBuilder mMenu;
private final Rect mContentRect;
- private final Rect mContentRectOnWindow;
- private final Rect mPreviousContentRectOnWindow;
- private final int[] mViewPosition;
- private final int[] mPreviousViewPosition;
- private final int[] mRootViewPosition;
- private final Rect mViewRect;
- private final Rect mPreviousViewRect;
+ private final Rect mContentRectOnScreen;
+ private final Rect mPreviousContentRectOnScreen;
+ private final int[] mViewPositionOnScreen;
+ private final int[] mPreviousViewPositionOnScreen;
+ private final int[] mRootViewPositionOnScreen;
+ private final Rect mViewRectOnScreen;
+ private final Rect mPreviousViewRectOnScreen;
private final Rect mScreenRect;
private final View mOriginatingView;
private final int mBottomAllowance;
@@ -77,16 +77,16 @@
MenuItem.SHOW_AS_ACTION_IF_ROOM);
setType(ActionMode.TYPE_FLOATING);
mContentRect = new Rect();
- mContentRectOnWindow = new Rect();
- mPreviousContentRectOnWindow = new Rect();
- mViewPosition = new int[2];
- mPreviousViewPosition = new int[2];
- mRootViewPosition = new int[2];
- mViewRect = new Rect();
- mPreviousViewRect = new Rect();
+ mContentRectOnScreen = new Rect();
+ mPreviousContentRectOnScreen = new Rect();
+ mViewPositionOnScreen = new int[2];
+ mPreviousViewPositionOnScreen = new int[2];
+ mRootViewPositionOnScreen = new int[2];
+ mViewRectOnScreen = new Rect();
+ mPreviousViewRectOnScreen = new Rect();
mScreenRect = new Rect();
mOriginatingView = Preconditions.checkNotNull(originatingView);
- mOriginatingView.getLocationInWindow(mViewPosition);
+ mOriginatingView.getLocationOnScreen(mViewPositionOnScreen);
// Allow the content rect to overshoot a little bit beyond the
// bottom view bound if necessary.
mBottomAllowance = context.getResources()
@@ -138,52 +138,53 @@
public void updateViewLocationInWindow() {
checkToolbarInitialized();
- mOriginatingView.getLocationInWindow(mViewPosition);
- mOriginatingView.getRootView().getLocationInWindow(mRootViewPosition);
- mOriginatingView.getGlobalVisibleRect(mViewRect);
- mViewRect.offset(mRootViewPosition[0], mRootViewPosition[1]);
+ mOriginatingView.getLocationOnScreen(mViewPositionOnScreen);
+ mOriginatingView.getRootView().getLocationOnScreen(mRootViewPositionOnScreen);
+ mOriginatingView.getGlobalVisibleRect(mViewRectOnScreen);
+ mViewRectOnScreen.offset(mRootViewPositionOnScreen[0], mRootViewPositionOnScreen[1]);
- if (!Arrays.equals(mViewPosition, mPreviousViewPosition)
- || !mViewRect.equals(mPreviousViewRect)) {
+ if (!Arrays.equals(mViewPositionOnScreen, mPreviousViewPositionOnScreen)
+ || !mViewRectOnScreen.equals(mPreviousViewRectOnScreen)) {
repositionToolbar();
- mPreviousViewPosition[0] = mViewPosition[0];
- mPreviousViewPosition[1] = mViewPosition[1];
- mPreviousViewRect.set(mViewRect);
+ mPreviousViewPositionOnScreen[0] = mViewPositionOnScreen[0];
+ mPreviousViewPositionOnScreen[1] = mViewPositionOnScreen[1];
+ mPreviousViewRectOnScreen.set(mViewRectOnScreen);
}
}
private void repositionToolbar() {
checkToolbarInitialized();
- mContentRectOnWindow.set(mContentRect);
- mContentRectOnWindow.offset(mViewPosition[0], mViewPosition[1]);
+ mContentRectOnScreen.set(mContentRect);
+ mContentRectOnScreen.offset(mViewPositionOnScreen[0], mViewPositionOnScreen[1]);
if (isContentRectWithinBounds()) {
mFloatingToolbarVisibilityHelper.setOutOfBounds(false);
// Make sure that content rect is not out of the view's visible bounds.
- mContentRectOnWindow.set(
- Math.max(mContentRectOnWindow.left, mViewRect.left),
- Math.max(mContentRectOnWindow.top, mViewRect.top),
- Math.min(mContentRectOnWindow.right, mViewRect.right),
- Math.min(mContentRectOnWindow.bottom, mViewRect.bottom + mBottomAllowance));
+ mContentRectOnScreen.set(
+ Math.max(mContentRectOnScreen.left, mViewRectOnScreen.left),
+ Math.max(mContentRectOnScreen.top, mViewRectOnScreen.top),
+ Math.min(mContentRectOnScreen.right, mViewRectOnScreen.right),
+ Math.min(mContentRectOnScreen.bottom,
+ mViewRectOnScreen.bottom + mBottomAllowance));
- if (!mContentRectOnWindow.equals(mPreviousContentRectOnWindow)) {
+ if (!mContentRectOnScreen.equals(mPreviousContentRectOnScreen)) {
// Content rect is moving.
mOriginatingView.removeCallbacks(mMovingOff);
mFloatingToolbarVisibilityHelper.setMoving(true);
mFloatingToolbarVisibilityHelper.updateToolbarVisibility();
mOriginatingView.postDelayed(mMovingOff, MOVING_HIDE_DELAY);
- mFloatingToolbar.setContentRect(mContentRectOnWindow);
+ mFloatingToolbar.setContentRect(mContentRectOnScreen);
mFloatingToolbar.updateLayout();
}
} else {
mFloatingToolbarVisibilityHelper.setOutOfBounds(true);
mFloatingToolbarVisibilityHelper.updateToolbarVisibility();
- mContentRectOnWindow.setEmpty();
+ mContentRectOnScreen.setEmpty();
}
- mPreviousContentRectOnWindow.set(mContentRectOnWindow);
+ mPreviousContentRectOnScreen.set(mContentRectOnScreen);
}
private boolean isContentRectWithinBounds() {
@@ -193,8 +194,8 @@
mContext.getResources().getDisplayMetrics().widthPixels,
mContext.getResources().getDisplayMetrics().heightPixels);
- return Rect.intersects(mContentRectOnWindow, mScreenRect)
- && Rect.intersects(mContentRectOnWindow, mViewRect);
+ return Rect.intersects(mContentRectOnScreen, mScreenRect)
+ && Rect.intersects(mContentRectOnScreen, mViewRectOnScreen);
}
@Override
@@ -269,7 +270,6 @@
mOriginatingView.removeCallbacks(mHideOff);
}
-
/**
* A helper for showing/hiding the floating toolbar depending on certain states.
*/
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index a6e8034..b3f688b 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -285,6 +285,7 @@
private final Context mContext;
private final View mParent;
+ private final int[] mParentPositionOnScreen = new int[2];
private final PopupWindow mPopupWindow;
private final ViewGroup mContentContainer;
private final int mMarginHorizontal;
@@ -337,8 +338,8 @@
}
};
- private final Rect mViewPort = new Rect();
- private final Point mCoords = new Point();
+ private final Rect mViewPortOnScreen = new Rect();
+ private final Point mCoordsOnScreen = new Point();
private final Rect mTmpRect = new Rect();
private final Region mTouchableRegion = new Region();
@@ -428,8 +429,8 @@
* Shows this popup at the specified coordinates.
* The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
*/
- public void show(Rect contentRect) {
- Preconditions.checkNotNull(contentRect);
+ public void show(Rect contentRectOnScreen) {
+ Preconditions.checkNotNull(contentRectOnScreen);
if (isShowing()) {
return;
@@ -447,9 +448,15 @@
// The "show" animation will make this visible.
mContentContainer.setAlpha(0);
}
- refreshCoordinatesAndOverflowDirection(contentRect);
+ refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
preparePopupContent();
- mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, mCoords.x, mCoords.y);
+ // We need to specify the offset relative to mParent.
+ // TODO: Consider to use PopupWindow.setLayoutInScreenEnabled(true) so that we can
+ // specify the popup poision in screen coordinates.
+ mParent.getLocationOnScreen(mParentPositionOnScreen);
+ final int relativeX = mCoordsOnScreen.x - mParentPositionOnScreen[0];
+ final int relativeY = mCoordsOnScreen.y - mParentPositionOnScreen[1];
+ mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, relativeX, relativeY);
setTouchableSurfaceInsetsComputer();
runShowAnimation();
}
@@ -502,17 +509,23 @@
* The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
* This is a no-op if this popup is not showing.
*/
- public void updateCoordinates(Rect contentRect) {
- Preconditions.checkNotNull(contentRect);
+ public void updateCoordinates(Rect contentRectOnScreen) {
+ Preconditions.checkNotNull(contentRectOnScreen);
if (!isShowing() || !mPopupWindow.isShowing()) {
return;
}
cancelOverflowAnimations();
- refreshCoordinatesAndOverflowDirection(contentRect);
+ refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
preparePopupContent();
- mPopupWindow.update(mCoords.x, mCoords.y, getWidth(), getHeight());
+ // We need to specify the offset relative to mParent.
+ // TODO: Consider to use PopupWindow.setLayoutInScreenEnabled(true) so that we can
+ // specify the popup poision in screen coordinates.
+ mParent.getLocationOnScreen(mParentPositionOnScreen);
+ final int relativeX = mCoordsOnScreen.x - mParentPositionOnScreen[0];
+ final int relativeY = mCoordsOnScreen.y - mParentPositionOnScreen[1];
+ mPopupWindow.update(relativeX, relativeY, getWidth(), getHeight());
}
/**
@@ -536,47 +549,47 @@
return mContext;
}
- private void refreshCoordinatesAndOverflowDirection(Rect contentRect) {
+ private void refreshCoordinatesAndOverflowDirection(Rect contentRectOnScreen) {
refreshViewPort();
- int x = contentRect.centerX() - getWidth() / 2;
+ int x = contentRectOnScreen.centerX() - getWidth() / 2;
// Update x so that the toolbar isn't rendered behind the nav bar in landscape.
- x = Math.max(0, Math.min(x, mViewPort.right - getWidth()));
+ x = Math.max(0, Math.min(x, mViewPortOnScreen.right - getWidth()));
int y;
- int availableHeightAboveContent = contentRect.top - mViewPort.top;
- int availableHeightBelowContent = mViewPort.bottom - contentRect.bottom;
+ int availableHeightAboveContent = contentRectOnScreen.top - mViewPortOnScreen.top;
+ int availableHeightBelowContent = mViewPortOnScreen.bottom - contentRectOnScreen.bottom;
if (mOverflowPanel == null) { // There is no overflow.
if (availableHeightAboveContent >= getToolbarHeightWithVerticalMargin()) {
// There is enough space at the top of the content.
- y = contentRect.top - getToolbarHeightWithVerticalMargin();
+ y = contentRectOnScreen.top - getToolbarHeightWithVerticalMargin();
} else if (availableHeightBelowContent >= getToolbarHeightWithVerticalMargin()) {
// There is enough space at the bottom of the content.
- y = contentRect.bottom;
+ y = contentRectOnScreen.bottom;
} else if (availableHeightBelowContent >= getEstimatedToolbarHeight(mContext)) {
// Just enough space to fit the toolbar with no vertical margins.
- y = contentRect.bottom - mMarginVertical;
+ y = contentRectOnScreen.bottom - mMarginVertical;
} else {
// Not enough space. Prefer to position as high as possible.
y = Math.max(
- mViewPort.top,
- contentRect.top - getToolbarHeightWithVerticalMargin());
+ mViewPortOnScreen.top,
+ contentRectOnScreen.top - getToolbarHeightWithVerticalMargin());
}
} else { // There is an overflow.
int margin = 2 * mMarginVertical;
int minimumOverflowHeightWithMargin = mOverflowPanel.getMinimumHeight() + margin;
- int availableHeightThroughContentDown =
- mViewPort.bottom - contentRect.top + getToolbarHeightWithVerticalMargin();
- int availableHeightThroughContentUp =
- contentRect.bottom - mViewPort.top + getToolbarHeightWithVerticalMargin();
+ int availableHeightThroughContentDown = mViewPortOnScreen.bottom -
+ contentRectOnScreen.top + getToolbarHeightWithVerticalMargin();
+ int availableHeightThroughContentUp = contentRectOnScreen.bottom -
+ mViewPortOnScreen.top + getToolbarHeightWithVerticalMargin();
if (availableHeightAboveContent >= minimumOverflowHeightWithMargin) {
// There is enough space at the top of the content rect for the overflow.
// Position above and open upwards.
updateOverflowHeight(availableHeightAboveContent - margin);
- y = contentRect.top - getHeight();
+ y = contentRectOnScreen.top - getHeight();
mOverflowDirection = OVERFLOW_DIRECTION_UP;
} else if (availableHeightAboveContent >= getToolbarHeightWithVerticalMargin()
&& availableHeightThroughContentDown >= minimumOverflowHeightWithMargin) {
@@ -584,33 +597,34 @@
// but not the overflow.
// Position above but open downwards.
updateOverflowHeight(availableHeightThroughContentDown - margin);
- y = contentRect.top - getToolbarHeightWithVerticalMargin();
+ y = contentRectOnScreen.top - getToolbarHeightWithVerticalMargin();
mOverflowDirection = OVERFLOW_DIRECTION_DOWN;
} else if (availableHeightBelowContent >= minimumOverflowHeightWithMargin) {
// There is enough space at the bottom of the content rect for the overflow.
// Position below and open downwards.
updateOverflowHeight(availableHeightBelowContent - margin);
- y = contentRect.bottom;
+ y = contentRectOnScreen.bottom;
mOverflowDirection = OVERFLOW_DIRECTION_DOWN;
} else if (availableHeightBelowContent >= getToolbarHeightWithVerticalMargin()
- && mViewPort.height() >= minimumOverflowHeightWithMargin) {
+ && mViewPortOnScreen.height() >= minimumOverflowHeightWithMargin) {
// There is enough space at the bottom of the content rect for the main panel
// but not the overflow.
// Position below but open upwards.
updateOverflowHeight(availableHeightThroughContentUp - margin);
- y = contentRect.bottom + getToolbarHeightWithVerticalMargin() - getHeight();
+ y = contentRectOnScreen.bottom + getToolbarHeightWithVerticalMargin() -
+ getHeight();
mOverflowDirection = OVERFLOW_DIRECTION_UP;
} else {
// Not enough space.
// Position at the top of the view port and open downwards.
- updateOverflowHeight(mViewPort.height() - margin);
- y = mViewPort.top;
+ updateOverflowHeight(mViewPortOnScreen.height() - margin);
+ y = mViewPortOnScreen.top;
mOverflowDirection = OVERFLOW_DIRECTION_DOWN;
}
mOverflowPanel.setOverflowDirection(mOverflowDirection);
}
- mCoords.set(x, y);
+ mCoordsOnScreen.set(x, y);
}
private int getToolbarHeightWithVerticalMargin() {
@@ -913,18 +927,18 @@
private void refreshViewPort() {
- mParent.getWindowVisibleDisplayFrame(mViewPort);
+ mParent.getWindowVisibleDisplayFrame(mViewPortOnScreen);
}
private boolean viewPortHasChanged() {
mParent.getWindowVisibleDisplayFrame(mTmpRect);
- return !mTmpRect.equals(mViewPort);
+ return !mTmpRect.equals(mViewPortOnScreen);
}
private int getToolbarWidth(int suggestedWidth) {
int width = suggestedWidth;
refreshViewPort();
- int maximumWidth = mViewPort.width() - 2 * mParent.getResources()
+ int maximumWidth = mViewPortOnScreen.width() - 2 * mParent.getResources()
.getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
if (width <= 0) {
width = mParent.getResources()
@@ -1443,6 +1457,9 @@
private static PopupWindow createPopupWindow(View content) {
ViewGroup popupContentHolder = new LinearLayout(content.getContext());
PopupWindow popupWindow = new PopupWindow(popupContentHolder);
+ // TODO: Use .setLayoutInScreenEnabled(true) instead of .setClippingEnabled(false)
+ // unless FLAG_LAYOUT_IN_SCREEN has any unintentional side-effects.
+ popupWindow.setClippingEnabled(false);
popupWindow.setWindowLayoutType(
WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
popupWindow.setAnimationStyle(0);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ba51b23..91b6348 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2436,7 +2436,7 @@
<!-- Allows managing (adding, removing) fingerprint templates. Reserved for the system. @hide -->
<permission android:name="android.permission.MANAGE_FINGERPRINT"
- android:protectionLevel="signature" />
+ android:protectionLevel="system|signature" />
<!-- Allows an application to control keyguard. Only allowed for system processes.
@hide -->
diff --git a/docs/html/about/versions/android-5.0-changes.jd b/docs/html/about/versions/android-5.0-changes.jd
index f51af40..25d253f 100644
--- a/docs/html/about/versions/android-5.0-changes.jd
+++ b/docs/html/about/versions/android-5.0-changes.jd
@@ -598,3 +598,8 @@
URI specifying where the photo should be stored. The camera app can write the
image to the location specified by that URI, and the app that fired the intent
would be able to read that file, even if the app is on the other profile. </p>
+
+<h3>Lockscreen widget support removed</h3>
+
+<p>Android 5.0 removes support for lockscreen widgets; it continues to support
+widgets on the home screen.</p>
\ No newline at end of file
diff --git a/docs/html/guide/components/bound-services.jd b/docs/html/guide/components/bound-services.jd
index 4215f0f2..c2ac607 100644
--- a/docs/html/guide/components/bound-services.jd
+++ b/docs/html/guide/components/bound-services.jd
@@ -649,8 +649,7 @@
<p>Additionally, if your service is started and accepts binding, then when the system calls
your {@link android.app.Service#onUnbind onUnbind()} method, you can optionally return
{@code true} if you would like to receive a call to {@link android.app.Service#onRebind
-onRebind()} the next time a client binds to the service (instead of receiving a call to {@link
-android.app.Service#onBind onBind()}). {@link android.app.Service#onRebind
+onRebind()} the next time a client binds to the service. {@link android.app.Service#onRebind
onRebind()} returns void, but the client still receives the {@link android.os.IBinder} in its
{@link android.content.ServiceConnection#onServiceConnected onServiceConnected()} callback.
Below, figure 1 illustrates the logic for this kind of lifecycle.</p>
diff --git a/docs/html/guide/topics/appwidgets/index.jd b/docs/html/guide/topics/appwidgets/index.jd
index a783ad1..c9575e0 100644
--- a/docs/html/guide/topics/appwidgets/index.jd
+++ b/docs/html/guide/topics/appwidgets/index.jd
@@ -26,12 +26,6 @@
</ol>
</li>
<li><a href="#preview">Setting a Preview Image</a></li>
- <li><a href="#lockscreen">Enabling App Widgets on the Lockscreen</a>
- <ol>
- <li><a href="#lockscreen-sizing">Sizing guidelines</a></li>
- </ol>
- </li>
-
<li><a href="#collections">Using App Widgets with Collections</a>
<ol>
<li><a href="#collection_sample">Sample application</a></li>
@@ -175,8 +169,7 @@
android:initialLayout="@layout/example_appwidget"
android:configure="com.example.android.ExampleAppWidgetConfigure"
android:resizeMode="horizontal|vertical"
- android:widgetCategory="home_screen|keyguard"
- android:initialKeyguardLayout="@layout/example_keyguard">
+ android:widgetCategory="home_screen">
</appwidget-provider>
</pre>
@@ -281,17 +274,11 @@
the widget can be resized. This field has no effect if it is greater than {@code minWidth} or if
horizontal resizing isn't enabled (see <code>resizeMode</code>). Introduced in Android 4.0.</li>
-<li>The <code>widgetCategory</code> attribute declares whether your App Widget can be displayed on the home screen,
-the lock screen (keyguard), or both. Values for this attribute include "home_screen" and "keyguard". A widget that
-is displayed on both needs to ensure that it follows the design guidelines for both widget classes. For more
-information, see <a href="#lockscreen">Enabling App Widgets on the Lockscreen</a>. The default value is "home_screen". Introduced in Android 4.2.
-</li>
-
-<li>The <code>initialKeyguardLayout</code> attribute points to the layout resource
-that defines the lock screen App Widget layout. This works the same way as the
-{@link android.appwidget.AppWidgetProviderInfo#initialLayout android:initialLayout},
-in that it provides a layout that can appear immediately until your app widget is initialized and able to update
-the layout. Introduced in Android 4.2.</li>
+<li>The <code>widgetCategory</code> attribute declares whether your App Widget
+can be displayed on the home screen ({@code home_screen}), the lock screen
+({@code keyguard}), or both. Only Android versions lower than 5.0 support
+lock-screen widgets. For Android 5.0 and higher, only {@code home_screen} is
+valid.</li>
</ul>
@@ -737,66 +724,6 @@
application and set it up how you'd like your preview image to appear, then save
it and place it in your application's drawable resources.</p>
-<h2 id="lockscreen">Enabling App Widgets on the Lockscreen</h2>
-
-<p>Android 4.2 introduces the ability for users to add widgets to the lock screen. To indicate that your app widget is available for use on the lock screen, declare the {@link android.appwidget.AppWidgetProviderInfo#widgetCategory android:widgetCategory} attribute in the XML file that specifies your {@link android.appwidget.AppWidgetProviderInfo}. This attribute supports two values: "home_screen" and "keyguard". An app widget can declare support for one or both.</p>
-
-<p>By default, every app widget supports placement on the Home screen, so "home_screen" is the default value for the
-{@link android.appwidget.AppWidgetProviderInfo#widgetCategory android:widgetCategory} attribute. If you want your app widget to be available for the lock screen, add the "keyguard" value:</p>
-<pre>
-<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
- ...
- android:widgetCategory="keyguard|home_screen">
-</appwidget-provider>
-</pre>
-
-<p>If you declare a widget to be displayable on both keyguard (lockscreen) and home, it's likely that you'll want to customize the widget depending on where it is displayed. For example, you might create a separate layout file for keyguard vs. home. The next step is to detect the widget category at runtime and respond accordingly.
-
-You can detect whether your widget is on the lockscreen or home screen by calling
-{@link android.appwidget.AppWidgetManager#getAppWidgetOptions getAppWidgetOptions()}
-to get the widget's options as a {@link android.os.Bundle}. The returned bundle will include the key
-{@link android.appwidget.AppWidgetManager#OPTION_APPWIDGET_HOST_CATEGORY}, whose value will be one of {@link android.appwidget.AppWidgetProviderInfo#WIDGET_CATEGORY_HOME_SCREEN} or
-{@link android.appwidget.AppWidgetProviderInfo#WIDGET_CATEGORY_KEYGUARD}. This value is determined by the host into which the widget is bound. In the {@link android.appwidget.AppWidgetProvider}, you can then check the widget's category, for example:</p>
-
-<pre>
-AppWidgetManager appWidgetManager;
-int widgetId;
-Bundle myOptions = appWidgetManager.getAppWidgetOptions (widgetId);
-
-// Get the value of OPTION_APPWIDGET_HOST_CATEGORY
-int category = myOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1);
-
-// If the value is WIDGET_CATEGORY_KEYGUARD, it's a lockscreen widget
-boolean isKeyguard = category == AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD;
-</pre>
-
-<p>Once you know the widget's category, you can optionally load a different base layout, set different properties, and so on. For example:</p>
-
-<pre>
-int baseLayout = isKeyguard ? R.layout.keyguard_widget_layout : R.layout.widget_layout;
-</pre>
-
-
-<p>You should also specify an initial layout for your app widget when on the lock screen with the
-{@link android.appwidget.AppWidgetProviderInfo#initialKeyguardLayout android:initialKeyguardLayout} attribute. This works the same way as the
-{@link android.appwidget.AppWidgetProviderInfo#initialLayout android:initialLayout}, in that it provides a layout that can appear immediately until your app widget is initialized and able to update the layout.</p>
-
-<h3 id="lockscreen-sizing">Sizing guidelines</h3>
-
-<p>When a widget is hosted on the lockscreen, the framework ignores the {@code minWidth}, {@code minHeight}, {@code minResizeWidth}, and {@code minResizeHeight} fields. If a widget is also a home screen widget, these parameters are still needed as they're still used on home, but they will be ignored for purposes of the lockscreen.</p>
-
-<p>The width of a lockscreen widget always fills the provided space. For the height of a lockscreen widget, you have the following options:</p>
-
-<ul>
- <li>If the widget does not mark itself as vertically resizable ({@code android:resizeMode="vertical"}), then the widget height will always be "small":
- <ul>
- <li>On a phone in portrait mode, "small" is defined as the space remaining when an unlock UI is being displayed.</li>
- <li>On tablets and landscape phones, "small" is set on a per-device basis.</li>
- </ul>
- </li>
- <li>If the widget marks itself as vertically resizable, then the widget height shows up as "small" on portrait phones displaying an unlock UI. In all other cases, the widget sizes to fill the available height.</li>
-</ul>
-
<h2 id="collections">Using App Widgets with Collections</h2>
<p>Android 3.0 introduces app widgets with collections. These kinds of App
diff --git a/docs/html/preview/api-overview.jd b/docs/html/preview/api-overview.jd
index 75b3c52..4300b43 100644
--- a/docs/html/preview/api-overview.jd
+++ b/docs/html/preview/api-overview.jd
@@ -120,14 +120,16 @@
<uses-permission
android:name="android.permission.USE_FINGERPRINT" />
</pre>
-
<img src="{@docRoot}preview/images/fingerprint-screen.png"
srcset="{@docRoot}preview/images/fingerprint-screen.png 1x, {@docRoot}preview/images/fingerprint-screen_2x.png 2x"
style="float:right; margin:0 0 10px 20px" width="282" height="476" />
<p>To see an app implementation of fingerprint authentication, refer to the
<a href="https://github.com/googlesamples/android-FingerprintDialog" class="external-link">
- Fingerprint Dialog sample</a>.</p>
+ Fingerprint Dialog sample</a>. For a demonstration of how you can use these authentication
+ APIs in conjunction with other Android APIs, see the video
+ <a class="video-shadowbox-button" href="https://www.youtube.com/watch?v=VOn7VrTRlA4">
+ Fingerprint and Payment APIs</a>.</p>
<p>If you are testing this feature, follow these steps:</p>
<ol>
diff --git a/docs/html/tools/debugging/ddms.jd b/docs/html/tools/debugging/ddms.jd
index 28ad11e..becbdb1 100644
--- a/docs/html/tools/debugging/ddms.jd
+++ b/docs/html/tools/debugging/ddms.jd
@@ -22,28 +22,23 @@
<li><a href="#logcat">Using LogCat</a></li>
<li><a href="#ops-location">Emulating phone operations and location</a></li>
</ol>
-
+
</li>
</ol>
</div>
</div>
- <p>Android ships with a debugging tool called the Dalvik Debug Monitor Server (DDMS), which
+ <p>Android Studio includes a debugging tool called the Dalvik Debug Monitor Server (DDMS), which
provides port-forwarding services, screen capture on the device, thread and heap information on
the device, logcat, process, and radio state information, incoming call and SMS spoofing,
location data spoofing, and more. This page provides a modest discussion of DDMS features; it is
not an exhaustive exploration of all the features and capabilities.</p>
-
+
<h2 id="running">Running DDMS</h2>
- <p>DDMS is integrated into Eclipse and is also shipped in the <code>tools/</code> directory of the
- SDK. DDMS works with both the emulator and a connected device. If both are connected and running simultaneously,
- DDMS defaults to the emulator.</p>
-
- <ul>
- <li>From Eclipse: Click <strong>Window > Open Perspective > Other... > DDMS</strong>.</li>
- <li>From the command line: Type <code>ddms</code> (or <code>./ddms</code> on Mac/Linux) from the <code>tools/</code>
- directory. </li>
- </ul>
+ <p>DDMS is integrated into Android Studio. To use it, launch the
+ <a href="{@docRoot}tools/help/monitor.html">Android Device Monitor</a>, and click the
+ <strong>DDMS</strong> menu button. DDMS works with both the emulator and a
+ connected device. If both are connected and running simultaneously, DDMS defaults to the emulator.</p>
<h2 id="how-ddms-works">How DDMS Interacts with a Debugger</h2>
@@ -70,7 +65,7 @@
all the VMs on a device. The traffic that is forwarded is determined by the currently selected process
in the DDMS Devices view.</p>
- <p>The following screenshot shows a typical DDMS screen in Eclipse. If you are starting DDMS from
+ <p>The following screenshot shows a typical DDMS screen. If you are starting DDMS from
the command line, the screen is slightly different, but much of the functionality is identical.
Notice that the highlighted process, <code>com.android.email</code>, that is running in the emulator
has the debugging port 8700 assigned to it as well as 8606. This signifies that DDMS is currently
@@ -78,10 +73,10 @@
<img src="{@docRoot}images/debug-ddms.png"
width="1024" />
- <p class="img-caption"><strong>Figure 1.</strong>
- Screenshot of DDMS</p>
+ <p class="img-caption"><strong>Figure 1.</strong>
+ Screenshot of DDMS</p>
- <p>If you are not using Eclipse and ADT, read <a href=
+ <p>If you are using the command line, read <a href=
"{@docRoot}tools/debugging/debugging-projects-cmdline.html#debuggingPort">Configuring
your IDE to attach to the debugging port</a>, for more information on attaching your
debugger.</p>
@@ -97,11 +92,11 @@
<h2 id="using-ddms">Using DDMS</h2>
The following sections describe how to use DDMS and the various tabs and panes that are part of the
- DDMS GUI. The Eclipse version and the command line version have minor UI differences, but the
- same functionality. For information on running DDMS, see the previous section in this document,
+ DDMS GUI. The Android Studio version and the command line version have minor UI differences, but
+ the same functionality. For information on running DDMS, see the previous section in this document,
<a href="#running">Running DDMS</a>.
-
-
+
+
<h3 id="heap">Viewing heap usage for a process</h3>
<p>DDMS allows you to view how much heap memory a process is using. This information is useful in
@@ -129,7 +124,7 @@
objects are being allocated when you perform certain actions in your application. This
information is valuable for assessing memory usage that can affect application performance.
</p>
-
+
<p>To track memory allocation of objects:</p>
<ol>
<li>In the Devices tab, select the process that you want to enable allocation tracking
@@ -155,7 +150,7 @@
<p>DDMS provides a File Explorer tab that allows you to view, copy, and delete files on the
device. This feature is useful in examining files that are created by your application or if you
want to transfer files to and from the device.</p>
-
+
<p>To work with an emulator or device's file system:</p>
<ol>
<li>In the Devices tab, select the emulator that you want to view the file system for.</li>
@@ -166,7 +161,7 @@
<li>To copy a file to the device, click the <strong>Push file</strong> button on the File
Explorer tab.</li>
</ol>
-
+
<!-- Need to elaborate more on where things are stored in the file system,
databases, apks, user info, files that are important to look at -->
@@ -189,7 +184,7 @@
where profiling data is collected, use the {@link android.os.Debug#startMethodTracing()} and
{@link android.os.Debug#stopMethodTracing()} methods. For more information about generating trace logs, see
<a href="debugging-tracing.html">Profiling and Debugging UIs</a>.</p>
-
+
<p>Before you start method profiling in DDMS, be aware of the following restrictions:</p>
<ul>
<li>Android 2.1 and earlier devices must
@@ -197,7 +192,7 @@
<li>Android 2.2 and later devices do not need an SD card. The trace log files are
streamed directly to your development machine.</li>
</ul>
-
+
<p>To start method profiling:</p>
<ol>
<li>On the Devices tab, select the process that you want to enable method profiling for.</li>
@@ -218,7 +213,7 @@
</ol>
<h3 id="network">Using the Network Traffic tool</h3>
-
+
<p>In Android 4.0, the DDMS (Dalvik Debug Monitor Server) includes a Detailed
Network Usage tab that makes it possible to track when your application is
making network requests. Using this tool, you can monitor how and when your app
@@ -274,7 +269,7 @@
<p>Socket tagging is supported in Android 4.0, but real-time stats will only be
displayed on devices running Android 4.0.3 or higher.</p>
-
+
<h3 id="logcat">Using LogCat</h3>
<p>LogCat is integrated into DDMS, and outputs the messages that you print out using the {@link android.util.Log}
@@ -296,7 +291,7 @@
<li>Error</li>
</ul>
-
+
<p>You can also setup your own custom filter to specify more details such as filtering messages
with the log tags or with the process id that generated the log message. The add filter,
edit filter, and delete filter buttons let you manage your custom filters.</p>
@@ -351,7 +346,7 @@
<li>KML - Keyhole Markup Language file</li>
</ul>
-
+
For more information about providing mock location data, see
<a href="{@docRoot}guide/topics/location/strategies.html#MockData">Location Strategies</a>.
diff --git a/docs/html/tools/debugging/debugging-tracing.jd b/docs/html/tools/debugging/debugging-tracing.jd
index fa5b4e1..829c82a 100644
--- a/docs/html/tools/debugging/debugging-tracing.jd
+++ b/docs/html/tools/debugging/debugging-tracing.jd
@@ -177,12 +177,15 @@
<h2 id="runningtraceview">Viewing Trace Files in Traceview</h2>
- <p>To run Traceview and view the trace files, enter <code>traceview
- <trace-base-name></code>. For example, to run Traceview on the example files copied in the
- previous section, use:</p>
- <pre>
-traceview /tmp/calc
-</pre>
+ <p>To run Traceview and view the trace files:</p>
+ <ul>
+ <li>start the
+ <a href="{@docRoot}tools/help/monitor.html">Android Device Monitor</a>. </li>
+ <li>In the Android Device Monitor tool bar, click <strong>DDMS</strong> and select a process. </li>
+ <li>Click the <strong>Start Method Profiling</strong> icon to start method profiling. </li>
+ <li>After the profiling is complete, click the <strong>Stop Method Profiling</strong> icon to
+ display the traceview. </li>
+ </ul>
<p class="note"><strong>Note:</strong> If you are trying to view the trace logs of an application
that is built with ProGuard enabled (release mode build), some method and member names might be obfuscated.
diff --git a/docs/html/tools/help/monitor.jd b/docs/html/tools/help/monitor.jd
index 513eb10..5f7b5ce 100644
--- a/docs/html/tools/help/monitor.jd
+++ b/docs/html/tools/help/monitor.jd
@@ -15,8 +15,8 @@
<p>Android Device Monitor is a stand-alone tool that provides a graphical user interface for
several Android application debugging and analysis tools. The Monitor tool does not
-require installation of an integrated development environment, such as Eclipse, and encapsulates the
-following tools:</p>
+require installation of an integrated development environment, such as Android Studio, and
+encapsulates the following tools:</p>
<ul>
<li><a href="{@docRoot}tools/debugging/ddms.html">DDMS</a></li>
@@ -30,9 +30,19 @@
<h2 id="usage">Usage</h2>
-<p>To start Device Monitor, enter the following command from the SDK <code>tools/</code>
-directory:</p>
+<p>To start Device Monitor:</p>
+
+<ul>
+ <li>From Android Studio, choose <strong>Tools > Android Device Monitor</strong> or click
+ the Android Device Monitor icon
+ <img src="{@docRoot}images/tools/hierarchicalviewer-icon.png" alt=""/>.
+ </li>
+ <li>From the command line, in the SDK <code>tools/</code> directory, enter the following command:
<pre>monitor</pre>
+ </li>
+</ul>
+
+
<p>Start an Android emulator or connect an Android device via USB cable, and connect Device
Monitor to the device by selecting it in the <strong>Devices</strong> window.</p>
diff --git a/docs/html/tools/help/traceview.jd b/docs/html/tools/help/traceview.jd
index 6555ac0..a2682913 100644
--- a/docs/html/tools/help/traceview.jd
+++ b/docs/html/tools/help/traceview.jd
@@ -3,14 +3,26 @@
parent.link=index.html
@jd:body
-<p>Traceview is a graphical viewer for execution logs saved by your application.
+<p>Traceview is a graphical viewer for execution logs saved by your application.
Traceview can help you debug your application and profile its performance.</p>
-<p>To start Traceview, enter the following command from the SDK <code>tools/</code> directory:</p>
- <pre>traceview</pre>
-</ol>
+<p>To start the Traceview tool:</p>
-<p>For more information on how to use Traceview, see
-<a href="{@docRoot}tools/debugging/debugging-tracing.html">Profiling with Traceview and dmtracedump</a>
+<ul>
+ <li>Start the <a href="{@docRoot}tools/help/monitor.html">Android Device Monitor</a>. </li>
+ <li>In the Android Device Monitor tool bar, click <strong>DDMS</strong> and select a process. </li>
+ <li>Click the <strong>Start Method Profiling</strong> icon to start method profiling. </li>
+ <li>After the profiling is complete, click the <strong>Stop Method Profiling</strong> icon to
+ display the traceview. </li>
+</ul>
+
+
+<p>For more information on how to use Traceview, see
+<a href="{@docRoot}tools/debugging/debugging-tracing.html">Profiling with Traceview and
+dmtracedump</a>.
</p>
+<p class="note"><strong>Note:</strong> Running <code>traceview</code> from the command line
+has been deprecated. </p>
+
+
diff --git a/docs/html/training/tv/start/layouts.jd b/docs/html/training/tv/start/layouts.jd
index a378096..2b190b4 100644
--- a/docs/html/training/tv/start/layouts.jd
+++ b/docs/html/training/tv/start/layouts.jd
@@ -262,14 +262,16 @@
<h2 id="advertising">Provide Effective Advertising</h2>
-<p>Advertising on Android TV must always be full-screen. Ads must not appear alongside or over
-content. The user must be able to dismiss an advertisement with the D-pad controller. Video ads must
-be dismissible within 30 seconds of their start time.</p>
+<p>For the living room environment, we recommend you use video ads solutions
+that are full-screen and dismissable within 30 seconds. Functionality for
+advertising on Android TV, such as dismiss buttons and clickthroughs, must be
+accessible using the D-pad rather than touch.</p>
-<p>Android TV does not provide a web browser. Your ads must not attempt to launch a web browser or
-redirect to the Google Play Store.</p>
+<p>Android TV does not provide a web browser. Your ads must not attempt to
+launch a web browser or redirect to Google Play Store content that is not
+approved for Android TV devices.</p>
<p class="note">
- <strong>Note:</strong> You can use the {@link android.webkit.WebView} class for logins to
- services like Google+ and Facebook.
+ <strong>Note:</strong> You can use the {@link android.webkit.WebView} class
+ for logins to services like Google+ and Facebook.
</p>
\ No newline at end of file
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 7c9b30b..26232a9 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -21,6 +21,7 @@
import android.content.res.ColorStateList;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -294,7 +295,13 @@
} else {
final PackageManager pm = context.getPackageManager();
try {
- mObj1 = pm.getResourcesForApplication(resPackage);
+ ApplicationInfo ai = pm.getApplicationInfo(
+ resPackage, PackageManager.GET_UNINSTALLED_PACKAGES);
+ if (ai != null) {
+ mObj1 = pm.getResourcesForApplication(ai);
+ } else {
+ break;
+ }
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, String.format("Unable to find pkg=%s for icon %s",
resPackage, this), e);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java
index 515be1d..5ce4fd2 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java
@@ -124,22 +124,27 @@
@Override
protected PrivateKey engineGeneratePrivate(KeySpec spec) throws InvalidKeySpecException {
- throw new UnsupportedOperationException(
- "To generate a key pair in Android KeyStore, use KeyPairGenerator initialized with"
+ throw new InvalidKeySpecException(
+ "To generate a key pair in Android Keystore, use KeyPairGenerator initialized with"
+ " " + KeyGenParameterSpec.class.getName());
}
@Override
protected PublicKey engineGeneratePublic(KeySpec spec) throws InvalidKeySpecException {
- throw new UnsupportedOperationException(
- "To generate a key pair in Android KeyStore, use KeyPairGenerator initialized with"
+ throw new InvalidKeySpecException(
+ "To generate a key pair in Android Keystore, use KeyPairGenerator initialized with"
+ " " + KeyGenParameterSpec.class.getName());
}
@Override
- protected Key engineTranslateKey(Key arg0) throws InvalidKeyException {
- throw new UnsupportedOperationException(
- "To import a key into Android KeyStore, use KeyStore.setEntry with "
- + KeyProtection.class.getName());
+ protected Key engineTranslateKey(Key key) throws InvalidKeyException {
+ if (key == null) {
+ throw new InvalidKeyException("key == null");
+ } else if ((!(key instanceof AndroidKeyStorePrivateKey))
+ && (!(key instanceof AndroidKeyStorePublicKey))) {
+ throw new InvalidKeyException(
+ "To import a key into Android Keystore, use KeyStore.setEntry");
+ }
+ return key;
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 9a2f908..11c22a9 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -185,15 +185,20 @@
@Override
protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException {
- throw new UnsupportedOperationException(
- "To generate secret key in Android KeyStore, use KeyGenerator initialized with "
+ throw new InvalidKeySpecException(
+ "To generate secret key in Android Keystore, use KeyGenerator initialized with "
+ KeyGenParameterSpec.class.getName());
}
@Override
protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException {
- throw new UnsupportedOperationException(
- "To import a secret key into Android KeyStore, use KeyStore.setEntry with "
- + KeyProtection.class.getName());
+ if (key == null) {
+ throw new InvalidKeyException("key == null");
+ } else if (!(key instanceof AndroidKeyStoreSecretKey)) {
+ throw new InvalidKeyException(
+ "To import a secret key into Android Keystore, use KeyStore.setEntry");
+ }
+
+ return key;
}
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index f42fab7..76d36a0 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -16,6 +16,8 @@
package android.media;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.util.Log;
import android.util.Pair;
import android.util.Range;
@@ -1003,6 +1005,7 @@
private Range<Rational> mAspectRatioRange;
private Range<Rational> mBlockAspectRatioRange;
private Range<Long> mBlocksPerSecondRange;
+ private Map<Size, Range<Long>> mMeasuredFrameRates;
private Range<Integer> mFrameRateRange;
private int mBlockWidth;
@@ -1195,6 +1198,28 @@
(double) mFrameRateRange.getUpper()));
}
+ @NonNull
+ private Size findClosestSize(int width, int height) {
+ int targetPixels = width * height;
+ Size closestSize = null;
+ int mimPixelsDiff = Integer.MAX_VALUE;
+ for (Size size : mMeasuredFrameRates.keySet()) {
+ int pixelsDiff = Math.abs(targetPixels - size.getWidth() * size.getHeight());
+ if (pixelsDiff < mimPixelsDiff) {
+ mimPixelsDiff = pixelsDiff;
+ closestSize = size;
+ }
+ }
+ return closestSize;
+ }
+
+ private Range<Double> estimateFrameRatesFor(int width, int height) {
+ Size size = findClosestSize(width, height);
+ Range<Long> range = mMeasuredFrameRates.get(size);
+ Double ratio = (double)(width * height) / (size.getWidth() * size.getHeight());
+ return Range.create(range.getLower() * ratio, range.getUpper() * ratio);
+ }
+
/**
* Returns the range of achievable video frame rates for a video size.
* May return {@code null}, if the codec did not publish any measurement
@@ -1208,12 +1233,18 @@
*
* @throws IllegalArgumentException if the video size is not supported.
*/
+ @Nullable
public Range<Double> getAchievableFrameRatesFor(int width, int height) {
if (!supports(width, height, null)) {
throw new IllegalArgumentException("unsupported size");
}
- // TODO: get this data from the codec
- return null;
+
+ if (mMeasuredFrameRates == null || mMeasuredFrameRates.size() <= 0) {
+ Log.w(TAG, "Codec did not publish any measurement data.");
+ return null;
+ }
+
+ return estimateFrameRatesFor(width, height);
}
/**
@@ -1346,6 +1377,34 @@
mSmallerDimensionUpperLimit = SIZE_RANGE.getUpper();
}
+ private Map<Size, Range<Long>> getMeasuredFrameRates(Map<String, Object> map) {
+ Map<Size, Range<Long>> ret = new HashMap<Size, Range<Long>>();
+ final String prefix = "measured-frame-rate-";
+ Set<String> keys = map.keySet();
+ for (String key : keys) {
+ // looking for: measured-frame-rate-WIDTHxHEIGHT-range
+ if (!key.startsWith(prefix)) {
+ continue;
+ }
+ String subKey = key.substring(prefix.length());
+ String[] temp = key.split("-");
+ if (temp.length != 5) {
+ continue;
+ }
+ String sizeStr = temp[3];
+ Size size = Utils.parseSize(sizeStr, null);
+ if (size == null || size.getWidth() * size.getHeight() <= 0) {
+ continue;
+ }
+ Range<Long> range = Utils.parseLongRange(map.get(key), null);
+ if (range == null || range.getLower() < 0 || range.getUpper() < 0) {
+ continue;
+ }
+ ret.put(size, range);
+ }
+ return ret;
+ }
+
private void parseFromInfo(MediaFormat info) {
final Map<String, Object> map = info.getMap();
Size blockSize = new Size(mBlockWidth, mBlockHeight);
@@ -1360,6 +1419,7 @@
counts = Utils.parseIntRange(map.get("block-count-range"), null);
blockRates =
Utils.parseLongRange(map.get("blocks-per-second-range"), null);
+ mMeasuredFrameRates = getMeasuredFrameRates(map);
{
Object o = map.get("size-range");
Pair<Size, Size> sizeRange = Utils.parseSizeRange(o);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 581c15b..6574e4e 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -67,6 +67,8 @@
import com.google.android.collect.Lists;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
@@ -1397,4 +1399,22 @@
}
return null; // not found
}
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("KeyguardUpdateMonitor state:");
+ pw.println(" SIM States:");
+ for (SimData data : mSimDatas.values()) {
+ pw.println(" " + data.toString());
+ }
+ pw.println(" Subs:");
+ if (mSubscriptionInfo != null) {
+ for (int i = 0; i < mSubscriptionInfo.size(); i++) {
+ pw.println(" " + mSubscriptionInfo.get(i));
+ }
+ }
+ pw.println(" Service states:");
+ for (int subId : mServiceStates.keySet()) {
+ pw.println(" " + subId + "=" + mServiceStates.get(subId));
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 048fb9a..348d0ec 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -625,9 +625,13 @@
mNetworkInfo != null && mNetworkInfo.getState() != State.DISCONNECTED;
}
+ public boolean isPasspoint() {
+ return mConfig != null && mConfig.isPasspoint();
+ }
+
/** Return whether the given {@link WifiInfo} is for this access point. */
private boolean isInfoForThisAccessPoint(WifiInfo info) {
- if (networkId != WifiConfiguration.INVALID_NETWORK_ID) {
+ if (isPasspoint() == false && networkId != WifiConfiguration.INVALID_NETWORK_ID) {
return networkId == info.getNetworkId();
} else {
// Might be an ephemeral connection with no WifiConfiguration. Try matching on SSID.
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b41e1ac..6e5dc3f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -51,7 +51,6 @@
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
- <uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
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 891cc73..f0e50f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -98,6 +98,7 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.keyguard.KeyguardHostView.OnDismissAction;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.DemoMode;
@@ -2704,6 +2705,9 @@
} else {
pw.println(" mHeadsUpManager: null");
}
+ if (KeyguardUpdateMonitor.getInstance(mContext) != null) {
+ KeyguardUpdateMonitor.getInstance(mContext).dump(fd, pw, args);
+ }
pw.println("SharedPreferences:");
for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index f0fc399..417f18d 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -48,6 +48,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.storage.MountServiceInternal;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -60,6 +61,7 @@
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.os.Zygote;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
@@ -245,6 +247,34 @@
scheduleFastWriteLocked();
}
}
+
+ MountServiceInternal mountServiceInternal = LocalServices.getService(
+ MountServiceInternal.class);
+ mountServiceInternal.addExternalStoragePolicy(
+ new MountServiceInternal.ExternalStorageMountPolicy() {
+ @Override
+ public int getMountMode(int uid, String packageName) {
+ if (Process.isIsolated(uid)) {
+ return Zygote.MOUNT_EXTERNAL_NONE;
+ }
+ if (noteOperation(AppOpsManager.OP_READ_EXTERNAL_STORAGE, uid,
+ packageName) != AppOpsManager.MODE_ALLOWED) {
+ return Zygote.MOUNT_EXTERNAL_NONE;
+ }
+ if (noteOperation(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE, uid,
+ packageName) != AppOpsManager.MODE_ALLOWED) {
+ return Zygote.MOUNT_EXTERNAL_READ;
+ }
+ return Zygote.MOUNT_EXTERNAL_WRITE;
+ }
+
+ @Override
+ public boolean hasExternalStorage(int uid, String packageName) {
+ final int mountMode = getMountMode(uid, packageName);
+ return mountMode == Zygote.MOUNT_EXTERNAL_READ
+ || mountMode == Zygote.MOUNT_EXTERNAL_WRITE;
+ }
+ });
}
public void packageRemoved(int uid, String packageName) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 39c2891..eb74ab0 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -407,6 +407,10 @@
// sequence number of NetworkRequests
private int mNextNetworkRequestId = 1;
+ // NetworkRequest activity String log entries.
+ private static final int MAX_NETWORK_REQUEST_LOGS = 20;
+ private final LocalLog mNetworkRequestInfoLogs = new LocalLog(MAX_NETWORK_REQUEST_LOGS);
+
// Array of <Network,ReadOnlyLocalLogs> tracking network validation and results
private static final int MAX_VALIDATION_LOGS = 10;
private final ArrayDeque<Pair<Network,ReadOnlyLocalLog>> mValidationLogs =
@@ -559,8 +563,9 @@
final DetailedState state = nai.networkInfo.getDetailedState();
for (int type = 0; type < mTypeLists.length; type++) {
final ArrayList<NetworkAgentInfo> list = mTypeLists[type];
+ final boolean contains = (list != null && list.contains(nai));
final boolean isFirst = (list != null && list.size() > 0 && nai == list.get(0));
- if (isFirst || isDefault) {
+ if (isFirst || (contains && isDefault)) {
maybeLogBroadcast(nai, state, type, isDefault);
sendLegacyNetworkBroadcast(nai, state, type);
}
@@ -609,8 +614,10 @@
if (DBG) log("ConnectivityService starting up");
mDefaultRequest = createInternetRequestForTransport(-1);
- mNetworkRequests.put(mDefaultRequest, new NetworkRequestInfo(
- null, mDefaultRequest, new Binder(), NetworkRequestInfo.REQUEST));
+ NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest,
+ new Binder(), NetworkRequestInfo.REQUEST);
+ mNetworkRequests.put(mDefaultRequest, defaultNRI);
+ mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI);
mDefaultMobileDataRequest = createInternetRequestForTransport(
NetworkCapabilities.TRANSPORT_CELLULAR);
@@ -1858,6 +1865,12 @@
pw.decreaseIndent();
}
}
+
+ pw.println();
+ pw.println("mNetworkRequestInfoLogs (most recent first):");
+ pw.increaseIndent();
+ mNetworkRequestInfoLogs.reverseDump(fd, pw, args);
+ pw.decreaseIndent();
}
}
@@ -2207,6 +2220,7 @@
private void handleRegisterNetworkRequest(NetworkRequestInfo nri) {
mNetworkRequests.put(nri.request, nri);
+ mNetworkRequestInfoLogs.log("REGISTER " + nri);
rematchAllNetworksAndRequests(null, 0);
if (nri.isRequest && mNetworkForRequestId.get(nri.request.requestId) == null) {
sendUpdatedScoreToFactories(nri.request, 0);
@@ -2256,6 +2270,7 @@
if (DBG) log("releasing NetworkRequest " + request);
nri.unlinkDeathRecipient();
mNetworkRequests.remove(request);
+ mNetworkRequestInfoLogs.log("RELEASE " + nri);
if (nri.isRequest) {
// Find all networks that are satisfying this request and remove the request
// from their request lists.
@@ -3528,8 +3543,9 @@
}
public String toString() {
- return (isRequest ? "Request" : "Listen") + " from uid/pid:" + mUid + "/" +
- mPid + " for " + request +
+ return (isRequest ? "Request" : "Listen") +
+ " from uid/pid:" + mUid + "/" + mPid +
+ " for " + request +
(mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
}
}
@@ -3559,9 +3575,9 @@
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
nextNetworkRequestId());
- if (DBG) log("requestNetwork for " + networkRequest);
NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
NetworkRequestInfo.REQUEST);
+ if (DBG) log("requestNetwork for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri));
if (timeoutMs > 0) {
@@ -3624,9 +3640,9 @@
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
nextNetworkRequestId());
- if (DBG) log("pendingRequest for " + networkRequest + " to trigger " + operation);
NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation,
NetworkRequestInfo.REQUEST);
+ if (DBG) log("pendingRequest for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT,
nri));
return networkRequest;
@@ -3674,11 +3690,11 @@
enforceAccessPermission();
}
- NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities(
- networkCapabilities), TYPE_NONE, nextNetworkRequestId());
- if (DBG) log("listenForNetwork for " + networkRequest);
+ NetworkRequest networkRequest = new NetworkRequest(
+ new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId());
NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
NetworkRequestInfo.LISTEN);
+ if (DBG) log("listenForNetwork for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
return networkRequest;
@@ -3692,11 +3708,11 @@
enforceAccessPermission();
}
- NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities(
- networkCapabilities), TYPE_NONE, nextNetworkRequestId());
- if (DBG) log("pendingListenForNetwork for " + networkRequest + " to trigger " + operation);
+ NetworkRequest networkRequest = new NetworkRequest(
+ new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId());
NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation,
NetworkRequestInfo.LISTEN);
+ if (DBG) log("pendingListenForNetwork for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
}
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index da552dd..bc61c3d3 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -68,6 +68,8 @@
import android.os.storage.IMountServiceListener;
import android.os.storage.IMountShutdownObserver;
import android.os.storage.IObbActionListener;
+import android.os.storage.MountServiceInternal;
+import android.os.storage.MountServiceInternal.ExternalStorageMountPolicy;
import android.os.storage.OnObbStateChangeListener;
import android.os.storage.StorageManager;
import android.os.storage.StorageResultCode;
@@ -127,6 +129,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -307,16 +310,6 @@
@GuardedBy("mLock")
private String mMoveTargetUuid;
- private DiskInfo findDiskById(String id) {
- synchronized (mLock) {
- final DiskInfo disk = mDisks.get(id);
- if (disk != null) {
- return disk;
- }
- }
- throw new IllegalArgumentException("No disk found for ID " + id);
- }
-
private VolumeInfo findVolumeByIdOrThrow(String id) {
synchronized (mLock) {
final VolumeInfo vol = mVolumes.get(id);
@@ -456,6 +449,9 @@
/** Map from raw paths to {@link ObbState}. */
final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
+ // Not guarded by a lock.
+ private final MountServiceInternalImpl mMountServiceInternal = new MountServiceInternalImpl();
+
class ObbState implements IBinder.DeathRecipient {
public ObbState(String rawPath, String canonicalPath, int callingUid,
IObbActionListener token, int nonce) {
@@ -807,7 +803,7 @@
for (int i = 0; i < mVolumes.size(); i++) {
final VolumeInfo vol = mVolumes.valueAt(i);
if (vol.isVisibleToUser(userId) && vol.isMountedReadable()) {
- final StorageVolume userVol = vol.buildStorageVolume(mContext, userId);
+ final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
final String envState = VolumeInfo.getEnvironmentForState(vol.getState());
@@ -1250,7 +1246,7 @@
// user-specific broadcasts.
for (int userId : mStartedUsers) {
if (vol.isVisibleToUser(userId)) {
- final StorageVolume userVol = vol.buildStorageVolume(mContext, userId);
+ final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv,
@@ -1370,6 +1366,8 @@
readSettingsLocked();
}
+ LocalServices.addService(MountServiceInternal.class, mMountServiceInternal);
+
/*
* Create the connection to vold with a maximum queue of twice the
* amount of containers we'd ever expect to have. This keeps an
@@ -1787,27 +1785,28 @@
}
}
- @Override
- public void remountUid(int uid) {
- enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+ private void remountUidExternalStorage(int uid, int mode) {
waitForReady();
- final int mountExternal = mPms.getMountExternalMode(uid);
- final String mode;
- if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
- mode = "default";
- } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
- mode = "read";
- } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
- mode = "write";
- } else {
- mode = "none";
+ String modeName = "none";
+ switch (mode) {
+ case Zygote.MOUNT_EXTERNAL_DEFAULT: {
+ modeName = "default";
+ } break;
+
+ case Zygote.MOUNT_EXTERNAL_READ: {
+ modeName = "read";
+ } break;
+
+ case Zygote.MOUNT_EXTERNAL_WRITE: {
+ modeName = "write";
+ } break;
}
try {
- mConnector.execute("volume", "remount_uid", uid, mode);
+ mConnector.execute("volume", "remount_uid", uid, modeName);
} catch (NativeDaemonConnectorException e) {
- Slog.w(TAG, "Failed to remount UID " + uid + " as " + mode + ": " + e);
+ Slog.w(TAG, "Failed to remount UID " + uid + " as " + modeName + ": " + e);
}
}
@@ -2598,15 +2597,20 @@
}
@Override
- public StorageVolume[] getVolumeList(int userId) {
+ public StorageVolume[] getVolumeList(int uid, String packageName) {
final ArrayList<StorageVolume> res = new ArrayList<>();
boolean foundPrimary = false;
+ final int userId = UserHandle.getUserId(uid);
+ final boolean reportUnmounted = !mMountServiceInternal.hasExternalStorage(
+ uid, packageName);
+
synchronized (mLock) {
for (int i = 0; i < mVolumes.size(); i++) {
final VolumeInfo vol = mVolumes.valueAt(i);
if (vol.isVisibleToUser(userId)) {
- final StorageVolume userVol = vol.buildStorageVolume(mContext, userId);
+ final StorageVolume userVol = vol.buildStorageVolume(mContext, userId,
+ reportUnmounted);
if (vol.isPrimary()) {
res.add(0, userVol);
foundPrimary = true;
@@ -3379,4 +3383,50 @@
mCryptConnector.monitor();
}
}
+
+ private final class MountServiceInternalImpl extends MountServiceInternal {
+ // Not guarded by a lock.
+ private final CopyOnWriteArrayList<ExternalStorageMountPolicy> mPolicies =
+ new CopyOnWriteArrayList<>();
+
+ @Override
+ public void addExternalStoragePolicy(ExternalStorageMountPolicy policy) {
+ // No locking - CopyOnWriteArrayList
+ mPolicies.add(policy);
+ }
+
+ @Override
+ public void onExternalStoragePolicyChanged(int uid, String packageName) {
+ final int mountMode = getExternalStorageMountMode(uid, packageName);
+ remountUidExternalStorage(uid, mountMode);
+ }
+
+ @Override
+ public int getExternalStorageMountMode(int uid, String packageName) {
+ // No locking - CopyOnWriteArrayList
+ int mountMode = Integer.MAX_VALUE;
+ for (ExternalStorageMountPolicy policy : mPolicies) {
+ final int policyMode = policy.getMountMode(uid, packageName);
+ if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) {
+ return Zygote.MOUNT_EXTERNAL_NONE;
+ }
+ mountMode = Math.min(mountMode, policyMode);
+ }
+ if (mountMode == Integer.MAX_VALUE) {
+ return Zygote.MOUNT_EXTERNAL_NONE;
+ }
+ return mountMode;
+ }
+
+ public boolean hasExternalStorage(int uid, String packageName) {
+ // No locking - CopyOnWriteArrayList
+ for (ExternalStorageMountPolicy policy : mPolicies) {
+ final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName);
+ if (!policyHasStorage) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 36d64aa..a06bb30 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.Manifest;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
@@ -360,12 +361,20 @@
+ " callback.asBinder=" + callback.asBinder());
}
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PHONE_STATE, null);
+ try {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "addOnSubscriptionsChangedListener");
+ // SKIP checking for run-time permission since obtained PRIVILEGED
+ } catch (SecurityException e) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PHONE_STATE,
+ "addOnSubscriptionsChangedListener");
- if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
- return;
+ if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
}
Record r;
@@ -471,9 +480,15 @@
checkListenerPermission(events);
if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
- if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
- return;
+ try {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
+ // SKIP checking for run-time permission since obtained PRIVILEGED
+ } catch (SecurityException e) {
+ if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
}
}
@@ -646,6 +661,12 @@
}
private boolean canReadPhoneState(String callingPackage) {
+ if (mContext.checkCallingPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) ==
+ PackageManager.PERMISSION_GRANTED) {
+ // SKIP checking for run-time permission since obtained PRIVILEGED
+ return true;
+ }
boolean canReadPhoneState = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED;
if (canReadPhoneState &&
@@ -1432,6 +1453,10 @@
intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
}
+ // Send broadcast twice, once for apps that have PRIVILEGED permission and once for those
+ // that have the runtime one
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
android.Manifest.permission.READ_PHONE_STATE,
AppOpsManager.OP_READ_PHONE_STATE);
@@ -1563,8 +1588,14 @@
}
if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PHONE_STATE, null);
+ try {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
+ // SKIP checking for run-time permission since obtained PRIVILEGED
+ } catch (SecurityException e) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PHONE_STATE, null);
+ }
}
if ((events & PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6e94647..5bfca10 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18,7 +18,10 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -62,6 +65,7 @@
import android.os.TransactionTooLargeException;
import android.os.WorkSource;
import android.os.storage.IMountService;
+import android.os.storage.MountServiceInternal;
import android.os.storage.StorageManager;
import android.service.voice.IVoiceInteractionSession;
import android.util.ArrayMap;
@@ -3219,7 +3223,10 @@
checkTime(startTime, "startProcess: getting gids from package manager");
final IPackageManager pm = AppGlobals.getPackageManager();
permGids = pm.getPackageGids(app.info.packageName, app.userId);
- mountExternal = pm.getMountExternalMode(uid);
+ MountServiceInternal mountServiceInternal = LocalServices.getService(
+ MountServiceInternal.class);
+ mountExternal = mountServiceInternal.getExternalStorageMountMode(uid,
+ app.info.packageName);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8c3a950..470bbb0e 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2669,8 +2669,13 @@
if (!r.finishing) {
if (!mService.isSleeping()) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + r);
- requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
- "stop-no-history", false);
+ if (requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
+ "stop-no-history", false)) {
+ // Activity was finished, no need to continue trying to schedule stop.
+ adjustFocusedActivityLocked(r, "stopActivityFinished");
+ r.resumeKeyDispatchingLocked();
+ return;
+ }
} else {
if (DEBUG_STATES) Slog.d(TAG_STATES, "Not finishing noHistory " + r
+ " on stop because we're just sleeping");
@@ -2963,6 +2968,7 @@
r.state = ActivityState.FINISHING;
if (mode == FINISH_IMMEDIATELY
+ || (mode == FINISH_AFTER_PAUSE && prevState == ActivityState.PAUSED)
|| prevState == ActivityState.STOPPED
|| prevState == ActivityState.INITIALIZING) {
// If this activity is already stopped, we can just finish
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 7a74729..e3c6037 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -29,6 +29,7 @@
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Build;
+import android.os.Debug;
import android.os.UserHandle;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
@@ -174,7 +175,7 @@
synchronized (mService.mPackages) {
for (PackageParser.Package pkg : mService.mPackages.values()) {
- if (!isSysComponentOrPersistentPrivApp(pkg)
+ if (!isSysComponentOrPersistentPlatformSignedPrivApp(pkg)
|| !doesPackageSupportRuntimePermissions(pkg)) {
continue;
}
@@ -344,7 +345,6 @@
Intent cbrIntent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
PackageParser.Package cbrPackage =
getDefaultSystemHandlerActivityPackageLPr(cbrIntent, userId);
-
if (cbrPackage != null && doesPackageSupportRuntimePermissions(cbrPackage)) {
grantRuntimePermissionsLPw(cbrPackage, SMS_PERMISSIONS, false, userId);
}
@@ -625,8 +625,9 @@
private PackageParser.Package getDefaultSystemHandlerActivityPackageLPr(
Intent intent, int userId) {
- List<ResolveInfo> handlers = mService.queryIntentActivities(intent,
- intent.resolveType(mService.mContext.getContentResolver()), 0, userId);
+ List<ResolveInfo> handlers = mService.mActivities.queryIntent(intent,
+ intent.resolveType(mService.mContext.getContentResolver()),
+ PackageManager.GET_DISABLED_COMPONENTS, userId);
final int handlerCount = handlers.size();
for (int i = 0; i < handlerCount; i++) {
ResolveInfo handler = handlers.get(i);
@@ -650,8 +651,9 @@
for (String syncAdapterPackageName : syncAdapterPackageNames) {
homeIntent.setPackage(syncAdapterPackageName);
- List<ResolveInfo> homeActivities = mService.queryIntentActivities(homeIntent,
- homeIntent.resolveType(mService.mContext.getContentResolver()), 0, userId);
+ List<ResolveInfo> homeActivities = mService.mActivities.queryIntent(homeIntent,
+ homeIntent.resolveType(mService.mContext.getContentResolver()),
+ PackageManager.GET_DISABLED_COMPONENTS, userId);
if (!homeActivities.isEmpty()) {
continue;
}
@@ -681,7 +683,7 @@
private PackageParser.Package getSystemPackageLPr(String packageName) {
PackageParser.Package pkg = getPackageLPr(packageName);
if (pkg != null && pkg.isSystemApp()) {
- return !isSysComponentOrPersistentPrivApp(pkg) ? pkg : null;
+ return !isSysComponentOrPersistentPlatformSignedPrivApp(pkg) ? pkg : null;
}
return null;
}
@@ -730,11 +732,16 @@
}
}
- private static boolean isSysComponentOrPersistentPrivApp(PackageParser.Package pkg) {
- return UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID
- || ((pkg.applicationInfo.privateFlags
- & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0
- && (pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0);
+ private boolean isSysComponentOrPersistentPlatformSignedPrivApp(PackageParser.Package pkg) {
+ if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {
+ return true;
+ }
+ if ((pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) == 0
+ || (pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
+ return false;
+ }
+ return PackageManagerService.compareSignatures(mService.mPlatformPackage.mSignatures,
+ pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
}
private static boolean doesPackageSupportRuntimePermissions(PackageParser.Package pkg) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 341568b..ef9bc8b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -62,6 +62,7 @@
import static android.content.pm.PackageManager.MOVE_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING;
import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Process.PACKAGE_INFO_GID;
@@ -143,10 +144,10 @@
import android.content.res.Resources;
import android.hardware.display.DisplayManager;
import android.net.Uri;
+import android.os.Debug;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
-import android.os.Debug;
import android.os.Environment;
import android.os.Environment.UserEnvironment;
import android.os.FileUtils;
@@ -166,6 +167,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.IMountService;
+import android.os.storage.MountServiceInternal;
import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
@@ -284,7 +286,7 @@
static final boolean DEBUG_PREFERRED = false;
static final boolean DEBUG_UPGRADE = false;
static final boolean DEBUG_DOMAIN_VERIFICATION = false;
- private static final boolean DEBUG_BACKUP = true;
+ private static final boolean DEBUG_BACKUP = false;
private static final boolean DEBUG_INSTALL = false;
private static final boolean DEBUG_REMOVE = false;
private static final boolean DEBUG_BROADCASTS = false;
@@ -2727,23 +2729,6 @@
return null;
}
- @Override
- public int getMountExternalMode(int uid) {
- if (Process.isIsolated(uid)) {
- return Zygote.MOUNT_EXTERNAL_NONE;
- } else {
- if (checkUidPermission(WRITE_MEDIA_STORAGE, uid) == PERMISSION_GRANTED) {
- return Zygote.MOUNT_EXTERNAL_DEFAULT;
- } else if (checkUidPermission(WRITE_EXTERNAL_STORAGE, uid) == PERMISSION_GRANTED) {
- return Zygote.MOUNT_EXTERNAL_WRITE;
- } else if (checkUidPermission(READ_EXTERNAL_STORAGE, uid) == PERMISSION_GRANTED) {
- return Zygote.MOUNT_EXTERNAL_READ;
- } else {
- return Zygote.MOUNT_EXTERNAL_DEFAULT;
- }
- }
- }
-
static PermissionInfo generatePermissionInfo(
BasePermission bp, int flags) {
if (bp.perm != null) {
@@ -3461,13 +3446,15 @@
// Only need to do this if user is initialized. Otherwise it's a new user
// and there are no processes running as the user yet and there's no need
// to make an expensive call to remount processes for the changed permissions.
- if ((READ_EXTERNAL_STORAGE.equals(name)
- || WRITE_EXTERNAL_STORAGE.equals(name))
- && sUserManager.isInitialized(userId)) {
+ if (READ_EXTERNAL_STORAGE.equals(name)
+ || WRITE_EXTERNAL_STORAGE.equals(name)) {
final long token = Binder.clearCallingIdentity();
try {
- final StorageManager storage = mContext.getSystemService(StorageManager.class);
- storage.remountUid(uid);
+ if (sUserManager.isInitialized(userId)) {
+ MountServiceInternal mountServiceInternal = LocalServices.getService(
+ MountServiceInternal.class);
+ mountServiceInternal.onExternalStoragePolicyChanged(uid, packageName);
+ }
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -4530,7 +4517,7 @@
} else if (result.size() <= 1) {
return result;
}
- result = filterCandidatesWithDomainPreferredActivitiesLPr(flags, result,
+ result = filterCandidatesWithDomainPreferredActivitiesLPr(intent, flags, result,
xpDomainInfo, userId);
Collections.sort(result, mResolvePrioritySorter);
}
@@ -4580,7 +4567,8 @@
if (ps == null) {
continue;
}
- int status = getDomainVerificationStatusLPr(ps, parentUserId);
+ long verificationState = getDomainVerificationStatusLPr(ps, parentUserId);
+ int status = (int)(verificationState >> 32);
if (result == null) {
result = new CrossProfileDomainInfo();
result.resolveInfo =
@@ -4647,9 +4635,11 @@
return scheme.equals(IntentFilter.SCHEME_HTTP) || scheme.equals(IntentFilter.SCHEME_HTTPS);
}
- private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(
- int flags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
+ private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
+ int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
int userId) {
+ final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0;
+
if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) {
Slog.v(TAG, "Filtering results with preferred activities. Candidates count: " +
candidates.size());
@@ -4677,11 +4667,17 @@
continue;
}
// Try to get the status from User settings first
- int status = getDomainVerificationStatusLPr(ps, userId);
+ long packedStatus = getDomainVerificationStatusLPr(ps, userId);
+ int status = (int)(packedStatus >> 32);
+ int linkGeneration = (int)(packedStatus & 0xFFFFFFFF);
if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.i(TAG, " + always: " + info.activityInfo.packageName);
+ Slog.i(TAG, " + always: " + info.activityInfo.packageName
+ + " : linkgen=" + linkGeneration);
}
+ // Use link-enabled generation as preferredOrder, i.e.
+ // prefer newly-enabled over earlier-enabled.
+ info.preferredOrder = linkGeneration;
alwaysList.add(info);
} else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
if (DEBUG_DOMAIN_VERIFICATION) {
@@ -4697,7 +4693,7 @@
}
}
}
- // First try to add the "always" resolution for the current user if there is any
+ // First try to add the "always" resolution(s) for the current user, if any
if (alwaysList.size() > 0) {
result.addAll(alwaysList);
// if there is an "always" for the parent user, add it.
@@ -4715,26 +4711,41 @@
result.add(xpDomainInfo.resolveInfo);
}
// Also add Browsers (all of them or only the default one)
- if ((flags & MATCH_ALL) != 0) {
+ if ((matchFlags & MATCH_ALL) != 0) {
result.addAll(matchAllList);
} else {
- // Try to add the Default Browser if we can
+ // Browser/generic handling case. If there's a default browser, go straight
+ // to that (but only if there is no other higher-priority match).
final String defaultBrowserPackageName = getDefaultBrowserPackageName(
UserHandle.myUserId());
- if (!TextUtils.isEmpty(defaultBrowserPackageName)) {
- boolean defaultBrowserFound = false;
- final int browserCount = matchAllList.size();
- for (int n=0; n<browserCount; n++) {
- ResolveInfo browser = matchAllList.get(n);
- if (browser.activityInfo.packageName.equals(defaultBrowserPackageName)) {
- result.add(browser);
- defaultBrowserFound = true;
- break;
+ int maxMatchPrio = 0;
+ ResolveInfo defaultBrowserMatch = null;
+ final int numCandidates = matchAllList.size();
+ for (int n = 0; n < numCandidates; n++) {
+ ResolveInfo info = matchAllList.get(n);
+ // track the highest overall match priority...
+ if (info.priority > maxMatchPrio) {
+ maxMatchPrio = info.priority;
+ }
+ // ...and the highest-priority default browser match
+ if (info.activityInfo.packageName.equals(defaultBrowserPackageName)) {
+ if (defaultBrowserMatch == null
+ || (defaultBrowserMatch.priority < info.priority)) {
+ if (debug) {
+ Slog.v(TAG, "Considering default browser match " + info);
+ }
+ defaultBrowserMatch = info;
}
}
- if (!defaultBrowserFound) {
- result.addAll(matchAllList);
+ }
+ if (defaultBrowserMatch != null
+ && defaultBrowserMatch.priority >= maxMatchPrio
+ && !TextUtils.isEmpty(defaultBrowserPackageName))
+ {
+ if (debug) {
+ Slog.v(TAG, "Default browser match " + defaultBrowserMatch);
}
+ result.add(defaultBrowserMatch);
} else {
result.addAll(matchAllList);
}
@@ -4758,15 +4769,19 @@
return result;
}
- private int getDomainVerificationStatusLPr(PackageSetting ps, int userId) {
- int status = ps.getDomainVerificationStatusForUser(userId);
+ // Returns a packed value as a long:
+ //
+ // high 'int'-sized word: link status: undefined/ask/never/always.
+ // low 'int'-sized word: relative priority among 'always' results.
+ private long getDomainVerificationStatusLPr(PackageSetting ps, int userId) {
+ long result = ps.getDomainVerificationStatusForUser(userId);
// if none available, get the master status
- if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+ if (result >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
if (ps.getIntentFilterVerificationInfo() != null) {
- status = ps.getIntentFilterVerificationInfo().getStatus();
+ result = ((long)ps.getIntentFilterVerificationInfo().getStatus()) << 32;
}
}
- return status;
+ return result;
}
private ResolveInfo querySkipCurrentProfileIntents(
@@ -12962,7 +12977,7 @@
false, //hidden
null, null, null,
false, // blockUninstall
- INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
+ INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);
if (!isSystemApp(ps)) {
if (ps.isAnyInstalled(sUserManager.getUserIds())) {
// Other user still have this package installed, so all
@@ -14438,6 +14453,33 @@
mInstallerService.systemReady();
mPackageDexOptimizer.systemReady();
+
+ MountServiceInternal mountServiceInternal = LocalServices.getService(
+ MountServiceInternal.class);
+ mountServiceInternal.addExternalStoragePolicy(
+ new MountServiceInternal.ExternalStorageMountPolicy() {
+ @Override
+ public int getMountMode(int uid, String packageName) {
+ if (Process.isIsolated(uid)) {
+ return Zygote.MOUNT_EXTERNAL_NONE;
+ }
+ if (checkUidPermission(WRITE_MEDIA_STORAGE, uid) == PERMISSION_GRANTED) {
+ return Zygote.MOUNT_EXTERNAL_DEFAULT;
+ }
+ if (checkUidPermission(READ_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
+ return Zygote.MOUNT_EXTERNAL_DEFAULT;
+ }
+ if (checkUidPermission(WRITE_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
+ return Zygote.MOUNT_EXTERNAL_READ;
+ }
+ return Zygote.MOUNT_EXTERNAL_WRITE;
+ }
+
+ @Override
+ public boolean hasExternalStorage(int uid, String packageName) {
+ return true;
+ }
+ });
}
@Override
@@ -14885,8 +14927,8 @@
pw.println();
count = 0;
for (PackageSetting ps : allPackageSettings) {
- final int status = ps.getDomainVerificationStatusForUser(userId);
- if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+ final long status = ps.getDomainVerificationStatusForUser(userId);
+ if (status >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
continue;
}
pw.println(prefix + "Package: " + ps.name);
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 6f46f69..4faf75a 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -341,7 +341,8 @@
void setUserState(int userId, int enabled, boolean installed, boolean stopped,
boolean notLaunched, boolean hidden,
String lastDisableAppCaller, ArraySet<String> enabledComponents,
- ArraySet<String> disabledComponents, boolean blockUninstall, int domainVerifState) {
+ ArraySet<String> disabledComponents, boolean blockUninstall, int domainVerifState,
+ int linkGeneration) {
PackageUserState state = modifyUserState(userId);
state.enabled = enabled;
state.installed = installed;
@@ -353,6 +354,7 @@
state.disabledComponents = disabledComponents;
state.blockUninstall = blockUninstall;
state.domainVerificationStatus = domainVerifState;
+ state.appLinkGeneration = linkGeneration;
}
ArraySet<String> getEnabledComponents(int userId) {
@@ -449,12 +451,23 @@
verificationInfo = info;
}
- int getDomainVerificationStatusForUser(int userId) {
- return readUserState(userId).domainVerificationStatus;
+ // Returns a packed value as a long:
+ //
+ // high 'int'-sized word: link status: undefined/ask/never/always.
+ // low 'int'-sized word: relative priority among 'always' results.
+ long getDomainVerificationStatusForUser(int userId) {
+ PackageUserState state = readUserState(userId);
+ long result = (long) state.appLinkGeneration;
+ result |= ((long) state.domainVerificationStatus) << 32;
+ return result;
}
- void setDomainVerificationStatusForUser(int status, int userId) {
- modifyUserState(userId).domainVerificationStatus = status;
+ void setDomainVerificationStatusForUser(final int status, int generation, int userId) {
+ PackageUserState state = modifyUserState(userId);
+ state.domainVerificationStatus = status;
+ if (status == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
+ state.appLinkGeneration = generation;
+ }
}
void clearDomainVerificationStatusForUser(int userId) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index bdcd714..312b7b3 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -87,6 +87,7 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.Xml;
import java.io.BufferedOutputStream;
@@ -196,6 +197,7 @@
private static final String ATTR_DOMAIN_VERIFICATON_STATE = "domainVerificationStatus";
private static final String ATTR_PACKAGE_NAME= "packageName";
private static final String ATTR_FINGERPRINT = "fingerprint";
+ private static final String ATTR_APP_LINK_GENERATION = "app-link-generation";
private final Object mLock;
@@ -294,6 +296,9 @@
// For every user, it is used to find the package name of the default Browser App.
final SparseArray<String> mDefaultBrowserApp = new SparseArray<String>();
+ // App-link priority tracking, per-user
+ final SparseIntArray mNextAppLinkGeneration = new SparseIntArray();
+
final StringBuilder mReadMessages = new StringBuilder();
/**
@@ -624,7 +629,7 @@
false, // hidden
null, null, null,
false, // blockUninstall
- INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
+ INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);
writePackageRestrictionsLPr(user.id);
}
}
@@ -1051,7 +1056,7 @@
}
return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
}
- int status = ps.getDomainVerificationStatusForUser(userId);
+ int status = (int)(ps.getDomainVerificationStatusForUser(userId) >> 32);
if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
if (ps.getIntentFilterVerificationInfo() != null) {
status = ps.getIntentFilterVerificationInfo().getStatus();
@@ -1060,7 +1065,7 @@
return status;
}
- boolean updateIntentFilterVerificationStatusLPw(String packageName, int status, int userId) {
+ boolean updateIntentFilterVerificationStatusLPw(String packageName, final int status, int userId) {
// Update the status for the current package
PackageSetting current = mPackages.get(packageName);
if (current == null) {
@@ -1070,7 +1075,15 @@
return false;
}
- current.setDomainVerificationStatusForUser(status, userId);
+ final int alwaysGeneration;
+ if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
+ alwaysGeneration = mNextAppLinkGeneration.get(userId) + 1;
+ mNextAppLinkGeneration.put(userId, alwaysGeneration);
+ } else {
+ alwaysGeneration = 0;
+ }
+
+ current.setDomainVerificationStatusForUser(status, alwaysGeneration, userId);
return true;
}
@@ -1382,7 +1395,7 @@
false, // hidden
null, null, null,
false, // blockUninstall
- INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
+ INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);
}
return;
}
@@ -1404,6 +1417,8 @@
return;
}
+ int maxAppLinkGeneration = 0;
+
int outerDepth = parser.getDepth();
PackageSetting ps = null;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1457,6 +1472,12 @@
PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED :
Integer.parseInt(verifStateStr);
+ final String linkGenStr = parser.getAttributeValue(null, ATTR_APP_LINK_GENERATION);
+ final int linkGeneration = linkGenStr == null ? 0 : Integer.parseInt(linkGenStr);
+ if (linkGeneration > maxAppLinkGeneration) {
+ maxAppLinkGeneration = linkGeneration;
+ }
+
ArraySet<String> enabledComponents = null;
ArraySet<String> disabledComponents = null;
@@ -1478,7 +1499,7 @@
ps.setUserState(userId, enabled, installed, stopped, notLaunched, hidden,
enabledCaller, enabledComponents, disabledComponents, blockUninstall,
- verifState);
+ verifState, linkGeneration);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
@@ -1496,6 +1517,8 @@
str.close();
+ mNextAppLinkGeneration.put(userId, maxAppLinkGeneration + 1);
+
} catch (XmlPullParserException e) {
mReadMessages.append("Error reading: " + e.toString());
PackageManagerService.reportSettingsProblem(Log.ERROR,
@@ -1749,6 +1772,10 @@
serializer.attribute(null, ATTR_DOMAIN_VERIFICATON_STATE,
Integer.toString(ustate.domainVerificationStatus));
}
+ if (ustate.appLinkGeneration != 0) {
+ serializer.attribute(null, ATTR_APP_LINK_GENERATION,
+ Integer.toString(ustate.appLinkGeneration));
+ }
if (ustate.enabledComponents != null
&& ustate.enabledComponents.size() > 0) {
serializer.startTag(null, TAG_ENABLED_COMPONENTS);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 87efb8d..9e41f70 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -44,6 +44,9 @@
import android.database.ContentObserver;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiPlaybackClient;
+import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
@@ -356,6 +359,7 @@
boolean mSystemReady;
boolean mSystemBooted;
boolean mHdmiPlugged;
+ HdmiControl mHdmiControl;
IUiModeManager mUiModeManager;
int mUiMode;
int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
@@ -1212,6 +1216,9 @@
}
private void handleShortPressOnHome() {
+ // Turn on the connected TV and switch HDMI input if we're a HDMI playback device.
+ getHdmiControl().turnOnTv();
+
// If there's a dream running then use home to escape the dream
// but don't actually go home.
if (mDreamManagerInternal != null && mDreamManagerInternal.isDreaming()) {
@@ -1223,6 +1230,46 @@
launchHomeFromHotKey();
}
+ /**
+ * Creates an accessor to HDMI control service that performs the operation of
+ * turning on TV (optional) and switching input to us. If HDMI control service
+ * is not available or we're not a HDMI playback device, the operation is no-op.
+ */
+ private HdmiControl getHdmiControl() {
+ if (null == mHdmiControl) {
+ HdmiControlManager manager = (HdmiControlManager) mContext.getSystemService(
+ Context.HDMI_CONTROL_SERVICE);
+ HdmiPlaybackClient client = null;
+ if (manager != null) {
+ client = manager.getPlaybackClient();
+ }
+ mHdmiControl = new HdmiControl(client);
+ }
+ return mHdmiControl;
+ }
+
+ private static class HdmiControl {
+ private final HdmiPlaybackClient mClient;
+
+ private HdmiControl(HdmiPlaybackClient client) {
+ mClient = client;
+ }
+
+ public void turnOnTv() {
+ if (mClient == null) {
+ return;
+ }
+ mClient.oneTouchPlay(new OneTouchPlayCallback() {
+ @Override
+ public void onComplete(int result) {
+ if (result != HdmiControlManager.RESULT_SUCCESS) {
+ Log.w(TAG, "One touch play failed: " + result);
+ }
+ }
+ });
+ }
+ }
+
private void handleLongPressOnHome(int deviceId) {
if (mLongPressOnHomeBehavior != LONG_PRESS_HOME_NOTHING) {
mHomeConsumed = true;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 89e9d2e..0bb04f8 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4230,7 +4230,7 @@
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#canChangeDtmfToneLength", e);
} catch (SecurityException e) {
- Log.w(TAG, "Permission error calling ITelephony#canChangeDtmfToneLength", e);
+ Log.e(TAG, "Permission error calling ITelephony#canChangeDtmfToneLength", e);
}
return false;
}
@@ -4248,6 +4248,8 @@
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#isWorldPhone", e);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Permission error calling ITelephony#isWorldPhone", e);
}
return false;
}
@@ -4265,6 +4267,8 @@
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#isTtyModeSupported", e);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Permission error calling ITelephony#isTtyModeSupported", e);
}
return false;
}
@@ -4283,6 +4287,8 @@
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#isHearingAidCompatibilitySupported", e);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Permission error calling ITelephony#isHearingAidCompatibilitySupported", e);
}
return false;
}