diff options
author | Jeff Sharkey <jsharkey@android.com> | 2013-10-24 15:40:15 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2013-10-24 15:40:15 -0700 |
commit | a5d24638f62b7bd5bdd7ec9d8ce14bccb21c3f47 (patch) | |
tree | 5df4c5e549c5af54f07ffc710933b398b3fe1ac5 /packages/DocumentsUI | |
parent | 6f90587eecd8e3970c7a87cf72512ae21c0a039b (diff) | |
parent | 180e484ef0370ab7f796c8fa51baf6b4c1c2cbdc (diff) | |
download | frameworks_base-a5d24638f62b7bd5bdd7ec9d8ce14bccb21c3f47.zip frameworks_base-a5d24638f62b7bd5bdd7ec9d8ce14bccb21c3f47.tar.gz frameworks_base-a5d24638f62b7bd5bdd7ec9d8ce14bccb21c3f47.tar.bz2 |
am 180e484e: am be416248: Merge "Remove persisted stacks when app removed/cleared." into klp-dev
* commit '180e484ef0370ab7f796c8fa51baf6b4c1c2cbdc':
Remove persisted stacks when app removed/cleared.
Diffstat (limited to 'packages/DocumentsUI')
3 files changed, 186 insertions, 1 deletions
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml index 6faf7f8..179bcd1 100644 --- a/packages/DocumentsUI/AndroidManifest.xml +++ b/packages/DocumentsUI/AndroidManifest.xml @@ -50,6 +50,14 @@ android:authorities="com.android.documentsui.recents" android:exported="false" /> + <receiver android:name=".PackageReceiver"> + <intent-filter> + <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" /> + <action android:name="android.intent.action.PACKAGE_DATA_CLEARED" /> + <data android:scheme="package" /> + </intent-filter> + </receiver> + <!-- TODO: remove when we have real clients --> <activity android:name=".TestActivity" android:enabled="false"> <intent-filter> diff --git a/packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java b/packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java new file mode 100644 index 0000000..aef63af --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui; + +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; + +/** + * Clean up {@link RecentsProvider} when packages are removed. + */ +public class PackageReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + final ContentResolver resolver = context.getContentResolver(); + + final String action = intent.getAction(); + if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) { + resolver.call(RecentsProvider.buildRecent(), RecentsProvider.METHOD_PURGE, null, null); + + } else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) { + final Uri data = intent.getData(); + if (data != null) { + final String packageName = data.getSchemeSpecificPart(); + resolver.call(RecentsProvider.buildRecent(), RecentsProvider.METHOD_PURGE_PACKAGE, + packageName, null); + } + } + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java index 4313fa7..f6e4349 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java @@ -16,24 +16,40 @@ package com.android.documentsui; +import static com.android.documentsui.model.DocumentInfo.getCursorString; + import android.content.ContentProvider; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; +import android.content.Intent; import android.content.UriMatcher; +import android.content.pm.ResolveInfo; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; +import android.os.Bundle; +import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; import android.text.format.DateUtils; import android.util.Log; +import com.android.documentsui.model.DocumentStack; +import com.android.documentsui.model.DurableUtils; +import com.android.internal.util.Predicate; +import com.google.android.collect.Sets; + +import libcore.io.IoUtils; + +import java.io.IOException; +import java.util.Set; + public class RecentsProvider extends ContentProvider { private static final String TAG = "RecentsProvider"; - public static final long MAX_HISTORY_IN_MILLIS = 45 * DateUtils.DAY_IN_MILLIS; + private static final long MAX_HISTORY_IN_MILLIS = 45 * DateUtils.DAY_IN_MILLIS; private static final String AUTHORITY = "com.android.documentsui.recents"; @@ -43,6 +59,9 @@ public class RecentsProvider extends ContentProvider { private static final int URI_STATE = 2; private static final int URI_RESUME = 3; + public static final String METHOD_PURGE = "purge"; + public static final String METHOD_PURGE_PACKAGE = "purgePackage"; + static { sMatcher.addURI(AUTHORITY, "recent", URI_RECENT); // state/authority/rootId/docId @@ -231,4 +250,116 @@ public class RecentsProvider extends ContentProvider { public int delete(Uri uri, String selection, String[] selectionArgs) { throw new UnsupportedOperationException("Unsupported Uri " + uri); } + + @Override + public Bundle call(String method, String arg, Bundle extras) { + if (METHOD_PURGE.equals(method)) { + // Purge references to unknown authorities + final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE); + final Set<String> knownAuth = Sets.newHashSet(); + for (ResolveInfo info : getContext() + .getPackageManager().queryIntentContentProviders(intent, 0)) { + knownAuth.add(info.providerInfo.authority); + } + + purgeByAuthority(new Predicate<String>() { + @Override + public boolean apply(String authority) { + // Purge unknown authorities + return !knownAuth.contains(authority); + } + }); + + return null; + + } else if (METHOD_PURGE_PACKAGE.equals(method)) { + // Purge references to authorities in given package + final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE); + intent.setPackage(arg); + final Set<String> packageAuth = Sets.newHashSet(); + for (ResolveInfo info : getContext() + .getPackageManager().queryIntentContentProviders(intent, 0)) { + packageAuth.add(info.providerInfo.authority); + } + + if (!packageAuth.isEmpty()) { + purgeByAuthority(new Predicate<String>() { + @Override + public boolean apply(String authority) { + // Purge authority matches + return packageAuth.contains(authority); + } + }); + } + + return null; + + } else { + return super.call(method, arg, extras); + } + } + + /** + * Purge all internal data whose authority matches the given + * {@link Predicate}. + */ + private void purgeByAuthority(Predicate<String> predicate) { + final SQLiteDatabase db = mHelper.getWritableDatabase(); + final DocumentStack stack = new DocumentStack(); + + Cursor cursor = db.query(TABLE_RECENT, null, null, null, null, null, null); + try { + while (cursor.moveToNext()) { + try { + final byte[] rawStack = cursor.getBlob( + cursor.getColumnIndex(RecentColumns.STACK)); + DurableUtils.readFromArray(rawStack, stack); + + if (stack.root != null && predicate.apply(stack.root.authority)) { + final String key = getCursorString(cursor, RecentColumns.KEY); + db.delete(TABLE_RECENT, RecentColumns.KEY + "=?", new String[] { key }); + } + } catch (IOException ignored) { + } + } + } finally { + IoUtils.closeQuietly(cursor); + } + + cursor = db.query(TABLE_STATE, new String[] { + StateColumns.AUTHORITY }, null, null, StateColumns.AUTHORITY, null, null); + try { + while (cursor.moveToNext()) { + final String authority = getCursorString(cursor, StateColumns.AUTHORITY); + if (predicate.apply(authority)) { + db.delete(TABLE_STATE, StateColumns.AUTHORITY + "=?", new String[] { + authority }); + Log.d(TAG, "Purged state for " + authority); + } + } + } finally { + IoUtils.closeQuietly(cursor); + } + + cursor = db.query(TABLE_RESUME, null, null, null, null, null, null); + try { + while (cursor.moveToNext()) { + try { + final byte[] rawStack = cursor.getBlob( + cursor.getColumnIndex(ResumeColumns.STACK)); + DurableUtils.readFromArray(rawStack, stack); + + if (stack.root != null && predicate.apply(stack.root.authority)) { + final String packageName = getCursorString( + cursor, ResumeColumns.PACKAGE_NAME); + db.delete(TABLE_RESUME, ResumeColumns.PACKAGE_NAME + "=?", + new String[] { packageName }); + } + } catch (IOException ignored) { + } + } + } finally { + IoUtils.closeQuietly(cursor); + } + } } |