Merge "Remove old animation" into klp-dev
diff --git a/api/current.txt b/api/current.txt
index ff72c4a..2ed9c58 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -29459,6 +29459,7 @@
ctor public AccessibilityNodeProvider();
method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo(int);
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String, int);
+ method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
method public boolean performAction(int, int, android.os.Bundle);
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 3b88973..961ee57 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -758,6 +758,14 @@
return true;
}
+ case APP_NOT_RESPONDING_VIA_PROVIDER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ appNotRespondingViaProvider(b);
+ reply.writeNoException();
+ return true;
+ }
+
case REMOVE_CONTENT_PROVIDER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
@@ -2891,6 +2899,7 @@
reply.recycle();
return res;
}
+
public void unstableProviderDied(IBinder connection) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -2902,6 +2911,18 @@
reply.recycle();
}
+ @Override
+ public void appNotRespondingViaProvider(IBinder connection) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(connection);
+ mRemote.transact(APP_NOT_RESPONDING_VIA_PROVIDER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
public void removeContentProvider(IBinder connection, boolean stable) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 6605b5b..02faeac 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4653,6 +4653,19 @@
}
}
+ final void appNotRespondingViaProvider(IBinder provider) {
+ synchronized (mProviderMap) {
+ ProviderRefCount prc = mProviderRefCountMap.get(provider);
+ if (prc != null) {
+ try {
+ ActivityManagerNative.getDefault()
+ .appNotRespondingViaProvider(prc.holder.connection);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
ContentProvider localProvider, IActivityManager.ContentProviderHolder holder) {
final String auths[] = PATTERN_SEMICOLON.split(holder.info.authority);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 0ba2ac5..300424c 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2200,5 +2200,10 @@
public void unstableProviderDied(IContentProvider icp) {
mMainThread.handleUnstableProviderDied(icp.asBinder(), true);
}
+
+ @Override
+ public void appNotRespondingViaProvider(IContentProvider icp) {
+ mMainThread.appNotRespondingViaProvider(icp.asBinder());
+ }
}
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 9a77377..dfea736 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -139,6 +139,7 @@
public boolean refContentProvider(IBinder connection, int stableDelta, int unstableDelta)
throws RemoteException;
public void unstableProviderDied(IBinder connection) throws RemoteException;
+ public void appNotRespondingViaProvider(IBinder connection) throws RemoteException;
public PendingIntent getRunningServiceControlPanel(ComponentName service)
throws RemoteException;
public ComponentName startService(IApplicationThread caller, Intent service,
@@ -691,4 +692,5 @@
int TAKE_PERSISTABLE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+179;
int RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+180;
int GET_PERSISTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+181;
+ int APP_NOT_RESPONDING_VIA_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+182;
}
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 0650798..cefc27f 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -16,15 +16,20 @@
package android.content;
+import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.DeadObjectException;
+import android.os.Handler;
import android.os.ICancellationSignal;
-import android.os.RemoteException;
+import android.os.Looper;
import android.os.ParcelFileDescriptor;
-import android.content.res.AssetFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
import dalvik.system.CloseGuard;
@@ -45,26 +50,64 @@
* until you are finished with the data they have returned.
*/
public class ContentProviderClient {
- private final IContentProvider mContentProvider;
+ private static final String TAG = "ContentProviderClient";
+
+ @GuardedBy("ContentProviderClient.class")
+ private static Handler sAnrHandler;
+
private final ContentResolver mContentResolver;
+ private final IContentProvider mContentProvider;
private final String mPackageName;
private final boolean mStable;
- private boolean mReleased;
private final CloseGuard mGuard = CloseGuard.get();
- /**
- * @hide
- */
- ContentProviderClient(ContentResolver contentResolver,
- IContentProvider contentProvider, boolean stable) {
- mContentProvider = contentProvider;
+ private long mAnrTimeout;
+ private NotRespondingRunnable mAnrRunnable;
+
+ private boolean mReleased;
+
+ /** {@hide} */
+ ContentProviderClient(
+ ContentResolver contentResolver, IContentProvider contentProvider, boolean stable) {
mContentResolver = contentResolver;
+ mContentProvider = contentProvider;
mPackageName = contentResolver.mPackageName;
mStable = stable;
+
mGuard.open("release");
}
+ /** {@hide} */
+ public void setDetectNotResponding(long timeoutMillis) {
+ synchronized (ContentProviderClient.class) {
+ mAnrTimeout = timeoutMillis;
+
+ if (timeoutMillis > 0) {
+ if (mAnrRunnable == null) {
+ mAnrRunnable = new NotRespondingRunnable();
+ }
+ if (sAnrHandler == null) {
+ sAnrHandler = new Handler(Looper.getMainLooper(), null, true /* async */);
+ }
+ } else {
+ mAnrRunnable = null;
+ }
+ }
+ }
+
+ private void beforeRemote() {
+ if (mAnrRunnable != null) {
+ sAnrHandler.postDelayed(mAnrRunnable, mAnrTimeout);
+ }
+ }
+
+ private void afterRemote() {
+ if (mAnrRunnable != null) {
+ sAnrHandler.removeCallbacks(mAnrRunnable);
+ }
+ }
+
/** See {@link ContentProvider#query ContentProvider.query} */
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder) throws RemoteException {
@@ -72,28 +115,31 @@
}
/** See {@link ContentProvider#query ContentProvider.query} */
- public Cursor query(Uri url, String[] projection, String selection,
- String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal)
- throws RemoteException {
- ICancellationSignal remoteCancellationSignal = null;
- if (cancellationSignal != null) {
- cancellationSignal.throwIfCanceled();
- remoteCancellationSignal = mContentProvider.createCancellationSignal();
- cancellationSignal.setRemote(remoteCancellationSignal);
- }
+ public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder, CancellationSignal cancellationSignal) throws RemoteException {
+ beforeRemote();
try {
- return mContentProvider.query(mPackageName, url, projection, selection, selectionArgs,
+ ICancellationSignal remoteCancellationSignal = null;
+ if (cancellationSignal != null) {
+ cancellationSignal.throwIfCanceled();
+ remoteCancellationSignal = mContentProvider.createCancellationSignal();
+ cancellationSignal.setRemote(remoteCancellationSignal);
+ }
+ return mContentProvider.query(mPackageName, url, projection, selection, selectionArgs,
sortOrder, remoteCancellationSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
+ } finally {
+ afterRemote();
}
}
/** See {@link ContentProvider#getType ContentProvider.getType} */
public String getType(Uri url) throws RemoteException {
+ beforeRemote();
try {
return mContentProvider.getType(url);
} catch (DeadObjectException e) {
@@ -101,11 +147,14 @@
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
+ } finally {
+ afterRemote();
}
}
/** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
+ beforeRemote();
try {
return mContentProvider.getStreamTypes(url, mimeTypeFilter);
} catch (DeadObjectException e) {
@@ -113,11 +162,14 @@
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
+ } finally {
+ afterRemote();
}
}
/** See {@link ContentProvider#canonicalize} */
public final Uri canonicalize(Uri url) throws RemoteException {
+ beforeRemote();
try {
return mContentProvider.canonicalize(mPackageName, url);
} catch (DeadObjectException e) {
@@ -125,11 +177,14 @@
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
+ } finally {
+ afterRemote();
}
}
/** See {@link ContentProvider#uncanonicalize} */
public final Uri uncanonicalize(Uri url) throws RemoteException {
+ beforeRemote();
try {
return mContentProvider.uncanonicalize(mPackageName, url);
} catch (DeadObjectException e) {
@@ -137,12 +192,14 @@
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
+ } finally {
+ afterRemote();
}
}
/** See {@link ContentProvider#insert ContentProvider.insert} */
- public Uri insert(Uri url, ContentValues initialValues)
- throws RemoteException {
+ public Uri insert(Uri url, ContentValues initialValues) throws RemoteException {
+ beforeRemote();
try {
return mContentProvider.insert(mPackageName, url, initialValues);
} catch (DeadObjectException e) {
@@ -150,11 +207,14 @@
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
+ } finally {
+ afterRemote();
}
}
/** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
+ beforeRemote();
try {
return mContentProvider.bulkInsert(mPackageName, url, initialValues);
} catch (DeadObjectException e) {
@@ -162,12 +222,15 @@
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
+ } finally {
+ afterRemote();
}
}
/** See {@link ContentProvider#delete ContentProvider.delete} */
public int delete(Uri url, String selection, String[] selectionArgs)
throws RemoteException {
+ beforeRemote();
try {
return mContentProvider.delete(mPackageName, url, selection, selectionArgs);
} catch (DeadObjectException e) {
@@ -175,12 +238,15 @@
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
+ } finally {
+ afterRemote();
}
}
/** See {@link ContentProvider#update ContentProvider.update} */
public int update(Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException {
+ beforeRemote();
try {
return mContentProvider.update(mPackageName, url, values, selection, selectionArgs);
} catch (DeadObjectException e) {
@@ -188,6 +254,8 @@
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
+ } finally {
+ afterRemote();
}
}
@@ -212,19 +280,22 @@
*/
public ParcelFileDescriptor openFile(Uri url, String mode, CancellationSignal signal)
throws RemoteException, FileNotFoundException {
- ICancellationSignal remoteSignal = null;
- if (signal != null) {
- signal.throwIfCanceled();
- remoteSignal = mContentProvider.createCancellationSignal();
- signal.setRemote(remoteSignal);
- }
+ beforeRemote();
try {
+ ICancellationSignal remoteSignal = null;
+ if (signal != null) {
+ signal.throwIfCanceled();
+ remoteSignal = mContentProvider.createCancellationSignal();
+ signal.setRemote(remoteSignal);
+ }
return mContentProvider.openFile(mPackageName, url, mode, remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
+ } finally {
+ afterRemote();
}
}
@@ -249,19 +320,22 @@
*/
public AssetFileDescriptor openAssetFile(Uri url, String mode, CancellationSignal signal)
throws RemoteException, FileNotFoundException {
- ICancellationSignal remoteSignal = null;
- if (signal != null) {
- signal.throwIfCanceled();
- remoteSignal = mContentProvider.createCancellationSignal();
- signal.setRemote(remoteSignal);
- }
+ beforeRemote();
try {
+ ICancellationSignal remoteSignal = null;
+ if (signal != null) {
+ signal.throwIfCanceled();
+ remoteSignal = mContentProvider.createCancellationSignal();
+ signal.setRemote(remoteSignal);
+ }
return mContentProvider.openAssetFile(mPackageName, url, mode, remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
+ } finally {
+ afterRemote();
}
}
@@ -275,13 +349,14 @@
public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
String mimeType, Bundle opts, CancellationSignal signal)
throws RemoteException, FileNotFoundException {
- ICancellationSignal remoteSignal = null;
- if (signal != null) {
- signal.throwIfCanceled();
- remoteSignal = mContentProvider.createCancellationSignal();
- signal.setRemote(remoteSignal);
- }
+ beforeRemote();
try {
+ ICancellationSignal remoteSignal = null;
+ if (signal != null) {
+ signal.throwIfCanceled();
+ remoteSignal = mContentProvider.createCancellationSignal();
+ signal.setRemote(remoteSignal);
+ }
return mContentProvider.openTypedAssetFile(
mPackageName, uri, mimeType, opts, remoteSignal);
} catch (DeadObjectException e) {
@@ -289,12 +364,15 @@
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
+ } finally {
+ afterRemote();
}
}
/** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
+ beforeRemote();
try {
return mContentProvider.applyBatch(mPackageName, operations);
} catch (DeadObjectException e) {
@@ -302,12 +380,14 @@
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
+ } finally {
+ afterRemote();
}
}
/** See {@link ContentProvider#call(String, String, Bundle)} */
- public Bundle call(String method, String arg, Bundle extras)
- throws RemoteException {
+ public Bundle call(String method, String arg, Bundle extras) throws RemoteException {
+ beforeRemote();
try {
return mContentProvider.call(mPackageName, method, arg, extras);
} catch (DeadObjectException e) {
@@ -315,6 +395,8 @@
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
+ } finally {
+ afterRemote();
}
}
@@ -359,7 +441,7 @@
}
/** {@hide} */
- public static void closeQuietly(ContentProviderClient client) {
+ public static void releaseQuietly(ContentProviderClient client) {
if (client != null) {
try {
client.release();
@@ -367,4 +449,12 @@
}
}
}
+
+ private class NotRespondingRunnable implements Runnable {
+ @Override
+ public void run() {
+ Log.w(TAG, "Detected provider not responding: " + mContentProvider);
+ mContentResolver.appNotRespondingViaProvider(mContentProvider);
+ }
+ }
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 95fb6858..916a6cd 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -287,6 +287,11 @@
/** @hide */
public abstract void unstableProviderDied(IContentProvider icp);
+ /** @hide */
+ public void appNotRespondingViaProvider(IContentProvider icp) {
+ throw new UnsupportedOperationException("appNotRespondingViaProvider");
+ }
+
/**
* Return the MIME type of the given content URL.
*
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 85ec803..8bf6e4f 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -635,17 +635,18 @@
documentUri.getAuthority());
try {
return getDocumentThumbnail(client, documentUri, size, signal);
- } catch (RemoteException e) {
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e);
return null;
} finally {
- ContentProviderClient.closeQuietly(client);
+ ContentProviderClient.releaseQuietly(client);
}
}
/** {@hide} */
public static Bitmap getDocumentThumbnail(
ContentProviderClient client, Uri documentUri, Point size, CancellationSignal signal)
- throws RemoteException {
+ throws RemoteException, IOException {
final Bundle openOpts = new Bundle();
openOpts.putParcelable(DocumentsContract.EXTRA_THUMBNAIL_SIZE, size);
@@ -693,9 +694,6 @@
}
return BitmapFactory.decodeFileDescriptor(fd, null, opts);
}
- } catch (IOException e) {
- Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e);
- return null;
} finally {
IoUtils.closeQuietly(afd);
}
@@ -717,55 +715,53 @@
parentDocumentUri.getAuthority());
try {
return createDocument(client, parentDocumentUri, mimeType, displayName);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to create document", e);
+ return null;
} finally {
- ContentProviderClient.closeQuietly(client);
+ ContentProviderClient.releaseQuietly(client);
}
}
/** {@hide} */
public static Uri createDocument(ContentProviderClient client, Uri parentDocumentUri,
- String mimeType, String displayName) {
+ String mimeType, String displayName) throws RemoteException {
final Bundle in = new Bundle();
in.putString(Document.COLUMN_DOCUMENT_ID, getDocumentId(parentDocumentUri));
in.putString(Document.COLUMN_MIME_TYPE, mimeType);
in.putString(Document.COLUMN_DISPLAY_NAME, displayName);
- try {
- final Bundle out = client.call(METHOD_CREATE_DOCUMENT, null, in);
- return buildDocumentUri(
- parentDocumentUri.getAuthority(), out.getString(Document.COLUMN_DOCUMENT_ID));
- } catch (Exception e) {
- Log.w(TAG, "Failed to create document", e);
- return null;
- }
+ final Bundle out = client.call(METHOD_CREATE_DOCUMENT, null, in);
+ return buildDocumentUri(
+ parentDocumentUri.getAuthority(), out.getString(Document.COLUMN_DOCUMENT_ID));
}
/**
* Delete the given document.
*
* @param documentUri document with {@link Document#FLAG_SUPPORTS_DELETE}
+ * @return if the document was deleted successfully.
*/
public static boolean deleteDocument(ContentResolver resolver, Uri documentUri) {
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
documentUri.getAuthority());
try {
- return deleteDocument(client, documentUri);
- } finally {
- ContentProviderClient.closeQuietly(client);
- }
- }
-
- /** {@hide} */
- public static boolean deleteDocument(ContentProviderClient client, Uri documentUri) {
- final Bundle in = new Bundle();
- in.putString(Document.COLUMN_DOCUMENT_ID, getDocumentId(documentUri));
-
- try {
- final Bundle out = client.call(METHOD_DELETE_DOCUMENT, null, in);
+ deleteDocument(client, documentUri);
return true;
} catch (Exception e) {
Log.w(TAG, "Failed to delete document", e);
return false;
+ } finally {
+ ContentProviderClient.releaseQuietly(client);
}
}
+
+ /** {@hide} */
+ public static void deleteDocument(ContentProviderClient client, Uri documentUri)
+ throws RemoteException {
+ final Bundle in = new Bundle();
+ in.putString(Document.COLUMN_DOCUMENT_ID, getDocumentId(documentUri));
+
+ client.call(METHOD_DELETE_DOCUMENT, null, in);
+ }
}
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index e835a97..41d3700 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -423,9 +423,15 @@
}
} break;
case AccessibilityNodeInfo.FOCUS_INPUT: {
- // Input focus cannot go to virtual views.
View target = root.findFocus();
- if (target != null && isShown(target)) {
+ if (target == null || !isShown(target)) {
+ break;
+ }
+ AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
+ if (provider != null) {
+ focused = provider.findFocus(focusType);
+ }
+ if (focused == null) {
focused = target.createAccessibilityNodeInfo();
}
} break;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 48f0f9e..06f00f7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5249,6 +5249,7 @@
info.setAccessibilityFocused(isAccessibilityFocused());
info.setSelected(isSelected());
info.setLongClickable(isLongClickable());
+ info.setLiveRegion(getAccessibilityLiveRegion());
// TODO: These make sense only if we are in an AdapterView but all
// views can be selected. Maybe from accessibility perspective
diff --git a/core/java/android/view/accessibility/AccessibilityNodeProvider.java b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
index 688cbdf..718c32f 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeProvider.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
@@ -132,4 +132,19 @@
int virtualViewId) {
return null;
}
+
+ /**
+ * Find the virtual view, i.e. a descendant of the host View, that has the
+ * specified focus type.
+ *
+ * @param focus The focus to find. One of
+ * {@link AccessibilityNodeInfo#FOCUS_INPUT} or
+ * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}.
+ * @return The node info of the focused view or null.
+ * @see AccessibilityNodeInfo#FOCUS_INPUT
+ * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY
+ */
+ public AccessibilityNodeInfo findFocus(int focus) {
+ return null;
+ }
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 05fd613..cd853b6 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -100,8 +100,20 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
- onCreate(savedInstanceState, makeMyIntent(),
- getResources().getText(com.android.internal.R.string.whichApplication),
+ // Use a specialized prompt when we're handling the 'Home' app startActivity()
+ final int titleResource;
+ final Intent intent = makeMyIntent();
+ final Set<String> categories = intent.getCategories();
+ if (Intent.ACTION_MAIN.equals(intent.getAction())
+ && categories != null
+ && categories.size() == 1
+ && categories.contains(Intent.CATEGORY_HOME)) {
+ titleResource = com.android.internal.R.string.whichHomeApplication;
+ } else {
+ titleResource = com.android.internal.R.string.whichApplication;
+ }
+
+ onCreate(savedInstanceState, intent, getResources().getText(titleResource),
null, null, true);
}
diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_dark.png
index 266ac98..7de0448 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_dark.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_light.png b/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_light.png
index 49b375f..5b916c9 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_light.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_dark.png
index af5c463..eafc553 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_dark.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_light.png b/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_light.png
index 819c552..6583e99 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_light.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_on_pressed_holo_dark.png b/core/res/res/drawable-xhdpi/btn_check_on_pressed_holo_dark.png
index 0e1b948..028eed6 100644
--- a/core/res/res/drawable-xhdpi/btn_check_on_pressed_holo_dark.png
+++ b/core/res/res/drawable-xhdpi/btn_check_on_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_on_pressed_holo_light.png b/core/res/res/drawable-xhdpi/btn_check_on_pressed_holo_light.png
index 8d8aabc..61efd3a 100644
--- a/core/res/res/drawable-xhdpi/btn_check_on_pressed_holo_light.png
+++ b/core/res/res/drawable-xhdpi/btn_check_on_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_on_pressed_holo_dark.png b/core/res/res/drawable-xxhdpi/btn_check_on_pressed_holo_dark.png
index 81b0f87..668548b 100644
--- a/core/res/res/drawable-xxhdpi/btn_check_on_pressed_holo_dark.png
+++ b/core/res/res/drawable-xxhdpi/btn_check_on_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6c334e2..d57c232 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3182,6 +3182,8 @@
<!-- Title of intent resolver dialog when selecting an application to run. -->
<string name="whichApplication">Complete action using</string>
+ <!-- Title of intent resolver dialog when selecting a HOME application to run. -->
+ <string name="whichHomeApplication">Select a home app</string>
<!-- Option to always use the selected application resolution in the future. See the "Complete action using" dialog title-->
<string name="alwaysUse">Use by default for this action.</string>
<!-- Text displayed when the user selects the check box for setting default application. See the "Use by default for this action" check box. -->
@@ -4449,6 +4451,9 @@
<!-- Print fail reason: unknown. [CHAR LIMIT=25] -->
<string name="reason_unknown">unknown</string>
+ <!-- Print fail reason: the print service that has to process the print job is not available. [CHAR LIMIT=none] -->
+ <string name="reason_service_unavailable">Print service not enabled</string>
+
<!-- Title for the notification that a print service was installed. [CHAR LIMIT=50] -->
<string name="print_service_installed_title"><xliff:g id="name" example="Cloud Print">%s</xliff:g> service installed</string>
<!-- Message for the notification that a print service was installed. [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c786888..3fdf6b9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -800,6 +800,7 @@
<java-symbol type="string" name="web_user_agent_target_content" />
<java-symbol type="string" name="webpage_unresponsive" />
<java-symbol type="string" name="whichApplication" />
+ <java-symbol type="string" name="whichHomeApplication" />
<java-symbol type="string" name="wifi_available_sign_in" />
<java-symbol type="string" name="network_available_sign_in" />
<java-symbol type="string" name="network_available_sign_in_detailed" />
@@ -927,6 +928,7 @@
<java-symbol type="string" name="mediasize_japanese_kahu" />
<java-symbol type="string" name="mediasize_japanese_kaku2" />
<java-symbol type="string" name="mediasize_japanese_you4" />
+ <java-symbol type="string" name="reason_service_unavailable" />
<java-symbol type="string" name="reason_unknown" />
<java-symbol type="string" name="restr_pin_enter_admin_pin" />
<java-symbol type="string" name="restr_pin_enter_pin" />
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 3425c91..84ea4c90 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -2687,7 +2687,11 @@
*/
private void queueMsgUnderWakeLock(Handler handler, int msg,
int arg1, int arg2, Object obj, int delay) {
+ final long ident = Binder.clearCallingIdentity();
+ // Always acquire the wake lock as AudioService because it is released by the
+ // message handler.
mAudioEventWakeLock.acquire();
+ Binder.restoreCallingIdentity(ident);
sendMsg(handler, msg, SENDMSG_QUEUE, arg1, arg2, obj, delay);
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index def9aa7..0abd5f8 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2162,6 +2162,7 @@
private static final int MEDIA_STARTED = 6;
private static final int MEDIA_PAUSED = 7;
private static final int MEDIA_STOPPED = 8;
+ private static final int MEDIA_SKIPPED = 9;
private static final int MEDIA_TIMED_TEXT = 99;
private static final int MEDIA_ERROR = 100;
private static final int MEDIA_INFO = 200;
@@ -2227,6 +2228,9 @@
if (mOnSeekCompleteListener != null) {
mOnSeekCompleteListener.onSeekComplete(mMediaPlayer);
}
+ // fall through
+
+ case MEDIA_SKIPPED:
if (mTimeProvider != null) {
mTimeProvider.onSeekComplete(mMediaPlayer);
}
@@ -2812,6 +2816,7 @@
private Handler mEventHandler;
private boolean mRefresh = false;
private boolean mPausing = false;
+ private boolean mSeeking = false;
private static final int NOTIFY = 1;
private static final int NOTIFY_TIME = 0;
private static final int REFRESH_AND_NOTIFY_TIME = 1;
@@ -2849,7 +2854,15 @@
}
private void scheduleNotification(int type, long delayUs) {
+ // ignore time notifications until seek is handled
+ if (mSeeking &&
+ (type == NOTIFY_TIME || type == REFRESH_AND_NOTIFY_TIME)) {
+ return;
+ }
+
if (DEBUG) Log.v(TAG, "scheduleNotification " + type + " in " + delayUs);
+ mStopped = type == NOTIFY_STOP;
+ mSeeking = type == NOTIFY_SEEK;
mEventHandler.removeMessages(NOTIFY);
Message msg = mEventHandler.obtainMessage(NOTIFY, type, 0);
mEventHandler.sendMessageDelayed(msg, (int) (delayUs / 1000));
@@ -2876,7 +2889,6 @@
synchronized(this) {
if (DEBUG) Log.d(TAG, "onPaused: " + paused);
if (mStopped) { // handle as seek if we were stopped
- mStopped = false;
scheduleNotification(NOTIFY_SEEK, 0 /* delay */);
} else {
mPausing = paused; // special handling if player disappeared
@@ -2890,7 +2902,6 @@
synchronized(this) {
if (DEBUG) Log.d(TAG, "onStopped");
mPaused = true;
- mStopped = true;
scheduleNotification(NOTIFY_STOP, 0 /* delay */);
}
}
@@ -2899,7 +2910,6 @@
@Override
public void onSeekComplete(MediaPlayer mp) {
synchronized(this) {
- mStopped = false;
scheduleNotification(NOTIFY_SEEK, 0 /* delay */);
}
}
@@ -2914,6 +2924,7 @@
}
private synchronized void notifySeek() {
+ mSeeking = false;
try {
long timeUs = getCurrentTimeUs(true, false);
if (DEBUG) Log.d(TAG, "onSeekComplete at " + timeUs);
@@ -3025,6 +3036,11 @@
}
long nextTimeUs = nowUs;
+ if (mSeeking) {
+ // skip timed-event notifications until seek is complete
+ return;
+ }
+
if (DEBUG) {
StringBuilder sb = new StringBuilder();
sb.append("notifyTimedEvent(").append(mLastTimeUs).append(" -> ")
@@ -3124,6 +3140,11 @@
if (monotonic && mLastTimeUs < mLastReportedTime) {
/* have to adjust time */
mTimeAdjustment = mLastReportedTime - mLastTimeUs;
+ if (mTimeAdjustment > 1000000) {
+ // schedule seeked event if time jumped significantly
+ // TODO: do this properly by introducing an exception
+ scheduleNotification(NOTIFY_SEEK, 0 /* delay */);
+ }
} else {
mTimeAdjustment = 0;
}
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 71a0567..6faf7f8 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -2,6 +2,7 @@
package="com.android.documentsui">
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
+ <uses-permission android:name="android.permission.REMOVE_TASKS" />
<application
android:name=".DocumentsApplication"
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index b7dcb71..92c30ba 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -64,6 +64,8 @@
<string name="save_error">Failed to save document</string>
<!-- Toast shown when creating a folder failed with an error [CHAR LIMIT=48] -->
<string name="create_error">Failed to create folder</string>
+ <!-- Error message shown when querying for a list of documents failed [CHAR LIMIT=48] -->
+ <string name="query_error">Failed to query documents</string>
<!-- Title of storage root location that contains recently modified or used documents [CHAR LIMIT=24] -->
<string name="root_recent">Recent</string>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index 48bfaf0..23a3f22 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -16,10 +16,13 @@
package com.android.documentsui;
+import static com.android.documentsui.DocumentsActivity.TAG;
+
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.FragmentManager;
+import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
@@ -29,6 +32,7 @@
import android.os.Bundle;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
@@ -36,8 +40,6 @@
import com.android.documentsui.model.DocumentInfo;
-import java.io.FileNotFoundException;
-
/**
* Dialog to create a new directory.
*/
@@ -88,12 +90,19 @@
final ContentResolver resolver = activity.getContentResolver();
final DocumentInfo cwd = activity.getCurrentDirectory();
- final Uri childUri = DocumentsContract.createDocument(
- resolver, cwd.derivedUri, Document.MIME_TYPE_DIR, mDisplayName);
+
+ ContentProviderClient client = null;
try {
+ client = DocumentsApplication.acquireUnstableProviderOrThrow(
+ resolver, cwd.derivedUri.getAuthority());
+ final Uri childUri = DocumentsContract.createDocument(
+ client, cwd.derivedUri, Document.MIME_TYPE_DIR, mDisplayName);
return DocumentInfo.fromUri(resolver, childUri);
- } catch (FileNotFoundException e) {
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to create directory", e);
return null;
+ } finally {
+ ContentProviderClient.releaseQuietly(client);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 1f11aed..6ff47f8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -31,6 +31,7 @@
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.LoaderManager.LoaderCallbacks;
+import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -259,7 +260,7 @@
public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
if (!isAdded()) return;
- mAdapter.swapCursor(result.cursor);
+ mAdapter.swapResult(result.cursor, result.exception);
// Push latest state up to UI
// TODO: if mode change was racing with us, don't overwrite it
@@ -285,7 +286,7 @@
@Override
public void onLoaderReset(Loader<DirectoryResult> loader) {
- mAdapter.swapCursor(null);
+ mAdapter.swapResult(null, null);
}
};
@@ -552,9 +553,16 @@
continue;
}
- if (!DocumentsContract.deleteDocument(resolver, doc.derivedUri)) {
+ ContentProviderClient client = null;
+ try {
+ client = DocumentsApplication.acquireUnstableProviderOrThrow(
+ resolver, doc.derivedUri.getAuthority());
+ DocumentsContract.deleteDocument(client, doc.derivedUri);
+ } catch (Exception e) {
Log.w(TAG, "Failed to delete " + doc);
hadTrouble = true;
+ } finally {
+ ContentProviderClient.releaseQuietly(client);
}
}
@@ -646,7 +654,7 @@
private List<Footer> mFooters = Lists.newArrayList();
- public void swapCursor(Cursor cursor) {
+ public void swapResult(Cursor cursor, Exception e) {
mCursor = cursor;
mCursorCount = cursor != null ? cursor.getCount() : 0;
@@ -667,6 +675,11 @@
}
}
+ if (e != null) {
+ mFooters.add(new MessageFooter(
+ 3, R.drawable.ic_dialog_alert, getString(R.string.query_error)));
+ }
+
if (isEmpty()) {
mEmptyView.setVisibility(View.VISIBLE);
} else {
@@ -971,19 +984,23 @@
@Override
protected Bitmap doInBackground(Uri... params) {
final Context context = mIconThumb.getContext();
+ final ContentResolver resolver = context.getContentResolver();
+ ContentProviderClient client = null;
Bitmap result = null;
try {
- // TODO: switch to using unstable provider
- result = DocumentsContract.getDocumentThumbnail(
- context.getContentResolver(), mUri, mThumbSize, mSignal);
+ client = DocumentsApplication.acquireUnstableProviderOrThrow(
+ resolver, mUri.getAuthority());
+ result = DocumentsContract.getDocumentThumbnail(client, mUri, mThumbSize, mSignal);
if (result != null) {
final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
context, mThumbSize);
thumbs.put(mUri, result);
}
} catch (Exception e) {
- Log.w(TAG, "Failed to load thumbnail: " + e);
+ Log.w(TAG, "Failed to load thumbnail for " + mUri + ": " + e);
+ } finally {
+ ContentProviderClient.releaseQuietly(client);
}
return result;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index 0b3ecf8..da0f526 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -56,7 +56,7 @@
@Override
public void close() {
IoUtils.closeQuietly(cursor);
- ContentProviderClient.closeQuietly(client);
+ ContentProviderClient.releaseQuietly(client);
cursor = null;
client = null;
}
@@ -158,7 +158,9 @@
+ result.mode + ", sortOrder=" + result.sortOrder);
try {
- result.client = resolver.acquireUnstableContentProviderClient(authority);
+ result.client = DocumentsApplication.acquireUnstableProviderOrThrow(
+ resolver, authority);
+
cursor = result.client.query(
mUri, null, null, null, getQuerySortOrder(result.sortOrder), mSignal);
cursor.registerContentObserver(mObserver);
@@ -177,7 +179,7 @@
} catch (Exception e) {
Log.w(TAG, "Failed to query", e);
result.exception = e;
- ContentProviderClient.closeQuietly(result.client);
+ ContentProviderClient.releaseQuietly(result.client);
} finally {
synchronized (this) {
mSignal = null;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 4caec8f..7a45641 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -35,6 +35,7 @@
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ComponentName;
+import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
@@ -878,6 +879,7 @@
mRoot.authority, mRoot.documentId);
return DocumentInfo.fromUri(getContentResolver(), uri);
} catch (FileNotFoundException e) {
+ Log.w(TAG, "Failed to find root", e);
return null;
}
}
@@ -1035,12 +1037,26 @@
@Override
protected Uri doInBackground(Void... params) {
+ final ContentResolver resolver = getContentResolver();
final DocumentInfo cwd = getCurrentDirectory();
- final Uri childUri = DocumentsContract.createDocument(
- getContentResolver(), cwd.derivedUri, mMimeType, mDisplayName);
+
+ ContentProviderClient client = null;
+ Uri childUri = null;
+ try {
+ client = DocumentsApplication.acquireUnstableProviderOrThrow(
+ resolver, cwd.derivedUri.getAuthority());
+ childUri = DocumentsContract.createDocument(
+ client, cwd.derivedUri, mMimeType, mDisplayName);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to create document", e);
+ } finally {
+ ContentProviderClient.releaseQuietly(client);
+ }
+
if (childUri != null) {
saveStackBlocking();
}
+
return childUri;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java
index 960181a..6b46e3a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java
@@ -19,13 +19,19 @@
import android.app.ActivityManager;
import android.app.Application;
import android.content.BroadcastReceiver;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Point;
import android.net.Uri;
+import android.os.RemoteException;
+import android.text.format.DateUtils;
public class DocumentsApplication extends Application {
+ private static final long PROVIDER_ANR_TIMEOUT = 20 * DateUtils.SECOND_IN_MILLIS;
+
private RootsCache mRoots;
private Point mThumbnailsSize;
private ThumbnailCache mThumbnails;
@@ -44,6 +50,17 @@
return thumbnails;
}
+ public static ContentProviderClient acquireUnstableProviderOrThrow(
+ ContentResolver resolver, String authority) throws RemoteException {
+ final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
+ authority);
+ if (client == null) {
+ throw new RemoteException("Failed to acquire provider for " + authority);
+ }
+ client.setDetectNotResponding(PROVIDER_ANR_TIMEOUT);
+ return client;
+ }
+
@Override
public void onCreate() {
final ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
index 9a4fb7d..47dbcdf 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
@@ -19,11 +19,12 @@
import static com.android.documentsui.DocumentsActivity.TAG;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_LAST_MODIFIED;
+import android.app.ActivityManager;
import android.content.AsyncTaskLoader;
import android.content.ContentProviderClient;
-import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.database.MergeCursor;
import android.net.Uri;
import android.os.Bundle;
@@ -56,9 +57,8 @@
public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
private static final boolean LOGD = true;
- // TODO: adjust for svelte devices
- // TODO: add support for oneway queries to avoid wedging loader
- private static final int MAX_OUTSTANDING_RECENTS = 2;
+ private static final int MAX_OUTSTANDING_RECENTS = 4;
+ private static final int MAX_OUTSTANDING_RECENTS_SVELTE = 2;
/**
* Time to wait for first pass to complete before returning partial results.
@@ -74,20 +74,29 @@
/** MIME types that should always be excluded from recents. */
private static final String[] RECENT_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
- private static final ExecutorService sExecutor = buildExecutor();
+ private static ExecutorService sExecutor;
/**
* Create a bounded thread pool for fetching recents; it creates threads as
* needed (up to maximum) and reclaims them when finished.
*/
- private static ExecutorService buildExecutor() {
- // Create a bounded thread pool for fetching recents; it creates
- // threads as needed (up to maximum) and reclaims them when finished.
- final ThreadPoolExecutor executor = new ThreadPoolExecutor(
- MAX_OUTSTANDING_RECENTS, MAX_OUTSTANDING_RECENTS, 10, TimeUnit.SECONDS,
- new LinkedBlockingQueue<Runnable>());
- executor.allowCoreThreadTimeOut(true);
- return executor;
+ private synchronized static ExecutorService getExecutor(Context context) {
+ if (sExecutor == null) {
+ final ActivityManager am = (ActivityManager) context.getSystemService(
+ Context.ACTIVITY_SERVICE);
+ final int maxOutstanding = am.isLowRamDevice() ? MAX_OUTSTANDING_RECENTS_SVELTE
+ : MAX_OUTSTANDING_RECENTS;
+
+ // Create a bounded thread pool for fetching recents; it creates
+ // threads as needed (up to maximum) and reclaims them when finished.
+ final ThreadPoolExecutor executor = new ThreadPoolExecutor(
+ maxOutstanding, maxOutstanding, 10, TimeUnit.SECONDS,
+ new LinkedBlockingQueue<Runnable>());
+ executor.allowCoreThreadTimeOut(true);
+ sExecutor = executor;
+ }
+
+ return sExecutor;
}
private final RootsCache mRoots;
@@ -120,25 +129,26 @@
public void run() {
if (isCancelled()) return;
- final ContentResolver resolver = getContext().getContentResolver();
- final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
- authority);
+ ContentProviderClient client = null;
try {
+ client = DocumentsApplication.acquireUnstableProviderOrThrow(
+ getContext().getContentResolver(), authority);
+
final Uri uri = DocumentsContract.buildRecentDocumentsUri(authority, rootId);
final Cursor cursor = client.query(
uri, null, null, null, DirectoryLoader.getQuerySortOrder(mSortOrder));
mWithRoot = new RootCursorWrapper(authority, rootId, cursor, MAX_DOCS_FROM_ROOT);
- set(mWithRoot);
-
- mFirstPassLatch.countDown();
- if (mFirstPassDone) {
- onContentChanged();
- }
-
} catch (Exception e) {
- setException(e);
+ Log.w(TAG, "Failed to load " + authority + ", " + rootId, e);
} finally {
- ContentProviderClient.closeQuietly(client);
+ ContentProviderClient.releaseQuietly(client);
+ }
+
+ set(mWithRoot);
+
+ mFirstPassLatch.countDown();
+ if (mFirstPassDone) {
+ onContentChanged();
}
}
@@ -156,6 +166,8 @@
@Override
public DirectoryResult loadInBackground() {
+ final ExecutorService executor = getExecutor(getContext());
+
if (mFirstPassLatch == null) {
// First time through we kick off all the recent tasks, and wait
// around to see if everyone finishes quickly.
@@ -170,7 +182,7 @@
mFirstPassLatch = new CountDownLatch(mTasks.size());
for (RecentTask task : mTasks.values()) {
- sExecutor.execute(task);
+ executor.execute(task);
}
try {
@@ -184,11 +196,14 @@
final long rejectBefore = System.currentTimeMillis() - REJECT_OLDER_THAN;
// Collect all finished tasks
+ boolean allDone = true;
List<Cursor> cursors = Lists.newArrayList();
for (RecentTask task : mTasks.values()) {
if (task.isDone()) {
try {
final Cursor cursor = task.get();
+ if (cursor == null) continue;
+
final FilteringCursorWrapper filtered = new FilteringCursorWrapper(
cursor, mState.acceptMimes, RECENT_REJECT_MIMES, rejectBefore) {
@Override
@@ -200,14 +215,16 @@
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
- Log.w(TAG, "Failed to load " + task.authority + ", " + task.rootId, e);
+ // We already logged on other side
}
+ } else {
+ allDone = false;
}
}
if (LOGD) {
Log.d(TAG, "Found " + cursors.size() + " of " + mTasks.size() + " recent queries done");
- Log.d(TAG, sExecutor.toString());
+ Log.d(TAG, executor.toString());
}
final DirectoryResult result = new DirectoryResult();
@@ -215,11 +232,18 @@
// Hint to UI if we're still loading
final Bundle extras = new Bundle();
- if (cursors.size() != mTasks.size()) {
+ if (!allDone) {
extras.putBoolean(DocumentsContract.EXTRA_LOADING, true);
}
- final MergeCursor merged = new MergeCursor(cursors.toArray(new Cursor[cursors.size()]));
+ final Cursor merged;
+ if (cursors.size() > 0) {
+ merged = new MergeCursor(cursors.toArray(new Cursor[cursors.size()]));
+ } else {
+ // Return something when nobody is ready
+ merged = new MatrixCursor(new String[0]);
+ }
+
final SortingCursorWrapper sorted = new SortingCursorWrapper(merged, result.sortOrder) {
@Override
public Bundle getExtras() {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index e3908e9..bad0a96 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -243,10 +243,11 @@
final List<RootInfo> roots = Lists.newArrayList();
final Uri rootsUri = DocumentsContract.buildRootsUri(authority);
- final ContentProviderClient client = resolver
- .acquireUnstableContentProviderClient(authority);
+
+ ContentProviderClient client = null;
Cursor cursor = null;
try {
+ client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority);
cursor = client.query(rootsUri, null, null, null, null);
while (cursor.moveToNext()) {
final RootInfo root = RootInfo.fromRootsCursor(authority, cursor);
@@ -256,7 +257,7 @@
Log.w(TAG, "Failed to load some roots from " + authority + ": " + e);
} finally {
IoUtils.closeQuietly(cursor);
- ContentProviderClient.closeQuietly(client);
+ ContentProviderClient.releaseQuietly(client);
}
return roots;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index 5091a61..91d9124 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -23,9 +23,10 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.DocumentsContract;
-import android.provider.DocumentsProvider;
import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsProvider;
+import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.RootCursorWrapper;
import libcore.io.IoUtils;
@@ -178,10 +179,11 @@
}
public void updateFromUri(ContentResolver resolver, Uri uri) throws FileNotFoundException {
- final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
- uri.getAuthority());
+ ContentProviderClient client = null;
Cursor cursor = null;
try {
+ client = DocumentsApplication.acquireUnstableProviderOrThrow(
+ resolver, uri.getAuthority());
cursor = client.query(uri, null, null, null, null);
if (!cursor.moveToFirst()) {
throw new FileNotFoundException("Missing details for " + uri);
@@ -191,7 +193,7 @@
throw asFileNotFoundException(t);
} finally {
IoUtils.closeQuietly(cursor);
- ContentProviderClient.closeQuietly(client);
+ ContentProviderClient.releaseQuietly(client);
}
}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
index 5a15cd2..e9f2c71 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
@@ -54,11 +54,20 @@
public class TestDocumentsProvider extends DocumentsProvider {
private static final String TAG = "TestDocuments";
- private static final boolean LAG_ROOTS = false;
- private static final boolean CRASH_ROOTS = false;
- private static final boolean REFRESH_ROOTS = false;
+ private static final boolean ROOTS_WEDGE = false;
+ private static final boolean ROOTS_LAG = false;
+ private static final boolean ROOTS_CRASH = false;
+ private static final boolean ROOTS_REFRESH = false;
- private static final boolean CRASH_DOCUMENT = false;
+ private static final boolean DOCUMENT_CRASH = false;
+
+ private static final boolean RECENT_WEDGE = false;
+
+ private static final boolean CHILD_WEDGE = false;
+ private static final boolean CHILD_CRASH = false;
+
+ private static final boolean THUMB_WEDGE = false;
+ private static final boolean THUMB_CRASH = false;
private static final String MY_ROOT_ID = "myRoot";
private static final String MY_DOC_ID = "myDoc";
@@ -95,10 +104,11 @@
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
Log.d(TAG, "Someone asked for our roots!");
- if (LAG_ROOTS) SystemClock.sleep(3000);
- if (CRASH_ROOTS) System.exit(12);
+ if (ROOTS_WEDGE) SystemClock.sleep(Integer.MAX_VALUE);
+ if (ROOTS_LAG) SystemClock.sleep(3000);
+ if (ROOTS_CRASH) System.exit(12);
- if (REFRESH_ROOTS) {
+ if (ROOTS_REFRESH) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
@@ -126,7 +136,7 @@
@Override
public Cursor queryDocument(String documentId, String[] projection)
throws FileNotFoundException {
- if (CRASH_DOCUMENT) System.exit(12);
+ if (DOCUMENT_CRASH) System.exit(12);
final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
includeFile(result, documentId, 0);
@@ -198,6 +208,9 @@
String parentDocumentId, String[] projection, String sortOrder)
throws FileNotFoundException {
+ if (CHILD_WEDGE) SystemClock.sleep(Integer.MAX_VALUE);
+ if (CHILD_CRASH) System.exit(12);
+
final ContentResolver resolver = getContext().getContentResolver();
final Uri notifyUri = DocumentsContract.buildDocumentUri(
"com.example.documents", parentDocumentId);
@@ -257,6 +270,9 @@
@Override
public Cursor queryRecentDocuments(String rootId, String[] projection)
throws FileNotFoundException {
+
+ if (RECENT_WEDGE) SystemClock.sleep(Integer.MAX_VALUE);
+
// Pretend to take a super long time to respond
SystemClock.sleep(3000);
@@ -275,6 +291,10 @@
@Override
public AssetFileDescriptor openDocumentThumbnail(
String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException {
+
+ if (THUMB_WEDGE) SystemClock.sleep(Integer.MAX_VALUE);
+ if (THUMB_CRASH) System.exit(12);
+
final Bitmap bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(bitmap);
final Paint paint = new Paint();
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index ddb6d1a..c1ac7d5 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -235,7 +235,7 @@
static final boolean DEBUG_USER_LEAVING = localLOGV || false;
static final boolean DEBUG_VISBILITY = localLOGV || false;
static final boolean DEBUG_PSS = localLOGV || false;
- static final boolean DEBUG_LOCKSCREEN = localLOGV || true;
+ static final boolean DEBUG_LOCKSCREEN = localLOGV || false;
static final boolean VALIDATE_TOKENS = true;
static final boolean SHOW_ACTIVITY_START_TIME = true;
@@ -7823,6 +7823,31 @@
}
}
+ @Override
+ public void appNotRespondingViaProvider(IBinder connection) {
+ enforceCallingPermission(
+ android.Manifest.permission.REMOVE_TASKS, "appNotRespondingViaProvider()");
+
+ final ContentProviderConnection conn = (ContentProviderConnection) connection;
+ if (conn == null) {
+ Slog.w(TAG, "ContentProviderConnection is null");
+ return;
+ }
+
+ final ProcessRecord host = conn.provider.proc;
+ if (host == null) {
+ Slog.w(TAG, "Failed to find hosting ProcessRecord");
+ return;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ appNotResponding(host, null, null, false, "ContentProvider not responding");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
public static final void installSystemProviders() {
List<ProviderInfo> providers;
synchronized (mSelf) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 4d66946..dd2f4c4 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -3438,23 +3438,22 @@
// Determine if the top task is exiting and should return to home. Do this before it gets
// removed in removeHistoryRecordsForAppsLocked.
boolean launchHomeNext = false;
- int top = mTaskHistory.size() - 1;
- while (top >= 0) {
- final TaskRecord topTask = mTaskHistory.get(top);
- if (topTask.mActivities.isEmpty()) {
- // Not possible, but just in case.
- --top;
+ TaskRecord topTask = mTaskHistory.get(mTaskHistory.size() - 1);
+ ArrayList<ActivityRecord> activities = topTask.mActivities;
+ int activityNdx;
+ for (activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = activities.get(activityNdx);
+ if (r.finishing) {
continue;
}
- ActivityRecord r = topTask.topRunningActivityLocked(null);
- if (r != null) {
- // r will be launched next.
+ if (r.app != app) {
+ // This is the dying activity.
break;
}
- // There is an activity in topTask that is finishing. If topTask belongs to the app
- // return to home depending on the task flag.
+ }
+ if (activityNdx < 0) {
+ // All activities in task belong to app. Set launchHomeNext to task's value.
launchHomeNext = topTask.mOnTopOfHome;
- break;
}
removeHistoryRecordsForAppLocked(app);
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index f718706..2db67d5 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1932,7 +1932,7 @@
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
final ActivityStack stack = mStacks.get(stackNdx);
// Only update launchHomeTaskNext for the focused stack.
- launchHomeTaskNext |= (stack == focusedStack && stack.handleAppDiedLocked(app));
+ launchHomeTaskNext |= (stack.handleAppDiedLocked(app) && stack == focusedStack);
}
if (!restarting) {
diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java
index bc70fe3..3b0ee24 100644
--- a/services/java/com/android/server/print/UserState.java
+++ b/services/java/com/android/server/print/UserState.java
@@ -134,6 +134,11 @@
}
if (service != null) {
service.onPrintJobQueued(printJob);
+ } else {
+ // The service for the job is no longer enabled, so just
+ // fail the job with the appropriate message.
+ mSpooler.setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
+ mContext.getString(R.string.reason_service_unavailable));
}
}
@@ -779,7 +784,7 @@
for (int i = 0; i < printJobCount; i++) {
PrintJobInfo printJob = printJobs.get(i);
mSpooler.setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
- mContext.getString(R.string.reason_unknown));
+ mContext.getString(R.string.reason_service_unavailable));
}
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 80c50cc..e6b0531 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -255,6 +255,9 @@
/** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000;
+ /** Amount of time (in milliseconds) to delay before declaring a starting window leaked. */
+ static final int STARTING_WINDOW_TIMEOUT_DURATION = 10000;
+
/**
* If true, the window manager will do its own custom freezing and general
* management of the screen during rotation.
@@ -2256,6 +2259,8 @@
token.appWindowToken.startingWindow = win;
if (DEBUG_STARTING_WINDOW) Slog.v (TAG, "addWindow: " + token.appWindowToken
+ " startingWindow=" + win);
+ Message m = mH.obtainMessage(H.REMOVE_STARTING_TIMEOUT, token.appWindowToken);
+ mH.sendMessageDelayed(m, STARTING_WINDOW_TIMEOUT_DURATION);
}
boolean imMayMove = true;
@@ -2356,6 +2361,10 @@
}
public void removeWindowLocked(Session session, WindowState win) {
+ if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
+ if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Starting window removed " + win);
+ removeStartingWindowTimeout(win.mAppToken);
+ }
if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && win==mCurrentFocus) Slog.v(
TAG, "Remove " + win + " client="
@@ -2498,6 +2507,7 @@
if (atoken != null) {
if (atoken.startingWindow == win) {
if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Nulling startingWindow " + win);
+ removeStartingWindowTimeout(atoken);
atoken.startingWindow = null;
} else if (atoken.allAppWindows.size() == 0 && atoken.startingData != null) {
// If this is the last window and we had requested a starting
@@ -2507,12 +2517,7 @@
} else if (atoken.allAppWindows.size() == 1 && atoken.startingView != null) {
// If this is the last window except for a starting transition
// window, we need to get rid of the starting transition.
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG, "Schedule remove starting " + token
- + ": no more real windows");
- }
- Message m = mH.obtainMessage(H.REMOVE_STARTING, atoken);
- mH.sendMessage(m);
+ scheduleRemoveStartingWindow(atoken);
}
}
@@ -3964,6 +3969,7 @@
if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) {
Slog.v(TAG, "Removing starting window: " + startingWindow);
}
+ removeStartingWindowTimeout(ttoken);
startingWindow.getWindowList().remove(startingWindow);
mWindowsChanged = true;
if (DEBUG_ADD_REMOVE) Slog.v(TAG,
@@ -4527,14 +4533,29 @@
}
Binder.restoreCallingIdentity(origId);
- if (startingToken != null) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Schedule remove starting "
- + startingToken + ": app token removed");
- Message m = mH.obtainMessage(H.REMOVE_STARTING, startingToken);
- mH.sendMessage(m);
+ // Will only remove if startingToken non null.
+ scheduleRemoveStartingWindow(startingToken);
+ }
+
+ void removeStartingWindowTimeout(AppWindowToken wtoken) {
+ if (wtoken != null) {
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG, Debug.getCallers(1) +
+ ": Remove starting window timeout " + wtoken + (wtoken != null ?
+ " startingWindow=" + wtoken.startingWindow : ""));
+ mH.removeMessages(H.REMOVE_STARTING_TIMEOUT, wtoken);
}
}
+ void scheduleRemoveStartingWindow(AppWindowToken wtoken) {
+ if (wtoken != null && wtoken.startingWindow != null) {
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG, Debug.getCallers(1) +
+ ": Schedule remove starting " + wtoken + (wtoken != null ?
+ " startingWindow=" + wtoken.startingWindow : ""));
+ removeStartingWindowTimeout(wtoken);
+ Message m = mH.obtainMessage(H.REMOVE_STARTING, wtoken);
+ mH.sendMessage(m);
+ }
+ }
private boolean tmpRemoveAppWindowsLocked(WindowToken token) {
final int NW = token.windows.size();
if (NW > 0) {
@@ -7053,6 +7074,8 @@
public static final int TAP_OUTSIDE_STACK = 31;
public static final int NOTIFY_ACTIVITY_DRAWN = 32;
+ public static final int REMOVE_STARTING_TIMEOUT = 33;
+
@Override
public void handleMessage(Message msg) {
if (DEBUG_WINDOW_TRACE) {
@@ -7151,6 +7174,7 @@
"Aborted starting " + wtoken
+ ": removed=" + wtoken.removed
+ " startingData=" + wtoken.startingData);
+ removeStartingWindowTimeout(wtoken);
wtoken.startingWindow = null;
wtoken.startingData = null;
abort = true;
@@ -7175,6 +7199,11 @@
}
} break;
+ case REMOVE_STARTING_TIMEOUT: {
+ final AppWindowToken wtoken = (AppWindowToken)msg.obj;
+ Slog.e(TAG, "Starting window " + wtoken + " timed out");
+ // Fall through.
+ }
case REMOVE_STARTING: {
final AppWindowToken wtoken = (AppWindowToken)msg.obj;
IBinder token = null;
@@ -9676,6 +9705,7 @@
winAnimator.mSurfaceShown = false;
winAnimator.mSurfaceControl = null;
winAnimator.mWin.mHasSurface = false;
+ scheduleRemoveStartingWindow(winAnimator.mWin.mAppToken);
}
try {