Loading packages/DocumentsUI/AndroidManifest.xml +8 −0 Original line number Diff line number Diff line Loading @@ -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> Loading packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java 0 → 100644 +46 −0 Original line number Diff line number Diff line /* * 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); } } } } packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java +132 −1 Original line number Diff line number Diff line Loading @@ -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"; Loading @@ -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 Loading Loading @@ -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); } } } Loading
packages/DocumentsUI/AndroidManifest.xml +8 −0 Original line number Diff line number Diff line Loading @@ -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> Loading
packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java 0 → 100644 +46 −0 Original line number Diff line number Diff line /* * 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); } } } }
packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java +132 −1 Original line number Diff line number Diff line Loading @@ -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"; Loading @@ -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 Loading Loading @@ -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); } } }