1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.documentsui; 18 19 import android.annotation.SuppressLint; 20 import android.app.ActivityManager; 21 import android.app.Application; 22 import android.content.BroadcastReceiver; 23 import android.content.ContentProviderClient; 24 import android.content.ContentResolver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.om.OverlayManager; 29 import android.net.Uri; 30 import android.os.RemoteException; 31 import android.os.UserHandle; 32 import android.text.format.DateUtils; 33 import android.util.Log; 34 35 import androidx.annotation.Nullable; 36 import androidx.localbroadcastmanager.content.LocalBroadcastManager; 37 38 import com.android.documentsui.base.Lookup; 39 import com.android.documentsui.base.UserId; 40 import com.android.documentsui.clipping.ClipStorage; 41 import com.android.documentsui.clipping.ClipStore; 42 import com.android.documentsui.clipping.DocumentClipper; 43 import com.android.documentsui.queries.SearchHistoryManager; 44 import com.android.documentsui.roots.ProvidersCache; 45 import com.android.documentsui.theme.ThemeOverlayManager; 46 import com.android.modules.utils.build.SdkLevel; 47 48 import com.google.common.collect.Lists; 49 50 import java.util.List; 51 52 import javax.annotation.concurrent.GuardedBy; 53 54 public class DocumentsApplication extends Application { 55 private static final String TAG = "DocumentsApplication"; 56 private static final long PROVIDER_ANR_TIMEOUT = 20 * DateUtils.SECOND_IN_MILLIS; 57 58 private static final List<String> PACKAGE_FILTER_ACTIONS = Lists.newArrayList( 59 Intent.ACTION_PACKAGE_ADDED, 60 Intent.ACTION_PACKAGE_CHANGED, 61 Intent.ACTION_PACKAGE_REMOVED, 62 Intent.ACTION_PACKAGE_DATA_CLEARED 63 ); 64 65 private static final List<String> PROFILE_FILTER_ACTIONS = Lists.newArrayList( 66 Intent.ACTION_MANAGED_PROFILE_ADDED, 67 Intent.ACTION_MANAGED_PROFILE_REMOVED, 68 Intent.ACTION_MANAGED_PROFILE_UNLOCKED, 69 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE 70 ); 71 72 @GuardedBy("DocumentsApplication.class") 73 @Nullable 74 private static volatile ConfigStore sConfigStore; 75 76 private ProvidersCache mProviders; 77 private ThumbnailCache mThumbnailCache; 78 private ClipStorage mClipStore; 79 private DocumentClipper mClipper; 80 private DragAndDropManager mDragAndDropManager; 81 private UserIdManager mUserIdManager; 82 private UserManagerState mUserManagerState; 83 private Lookup<String, String> mFileTypeLookup; 84 getProvidersCache(Context context)85 public static ProvidersCache getProvidersCache(Context context) { 86 return ((DocumentsApplication) context.getApplicationContext()).mProviders; 87 } 88 getThumbnailCache(Context context)89 public static ThumbnailCache getThumbnailCache(Context context) { 90 final DocumentsApplication app = (DocumentsApplication) context.getApplicationContext(); 91 return app.mThumbnailCache; 92 } 93 acquireUnstableProviderOrThrow( ContentResolver resolver, String authority)94 public static ContentProviderClient acquireUnstableProviderOrThrow( 95 ContentResolver resolver, String authority) throws RemoteException { 96 final ContentProviderClient client = resolver.acquireUnstableContentProviderClient( 97 authority); 98 if (client == null) { 99 throw new RemoteException("Failed to acquire provider for " + authority); 100 } 101 client.setDetectNotResponding(PROVIDER_ANR_TIMEOUT); 102 return client; 103 } 104 getDocumentClipper(Context context)105 public static DocumentClipper getDocumentClipper(Context context) { 106 return ((DocumentsApplication) context.getApplicationContext()).mClipper; 107 } 108 getClipStore(Context context)109 public static ClipStore getClipStore(Context context) { 110 return ((DocumentsApplication) context.getApplicationContext()).mClipStore; 111 } 112 getUserIdManager(Context context)113 public static UserIdManager getUserIdManager(Context context) { 114 UserIdManager userIdManager = 115 ((DocumentsApplication) context.getApplicationContext()).mUserIdManager; 116 if (userIdManager == null) { 117 userIdManager = UserIdManager.create(context); 118 ((DocumentsApplication) context.getApplicationContext()).mUserIdManager = userIdManager; 119 } 120 return userIdManager; 121 } 122 123 /** 124 * UserManagerState class is used to maintain the list of userIds and other details like 125 * cross profile access, label and badge associated with these userIds. 126 */ getUserManagerState(Context context)127 public static UserManagerState getUserManagerState(Context context) { 128 UserManagerState userManagerState = 129 ((DocumentsApplication) context.getApplicationContext()).mUserManagerState; 130 if (userManagerState == null && getConfigStore().isPrivateSpaceInDocsUIEnabled() 131 && SdkLevel.isAtLeastS()) { 132 userManagerState = UserManagerState.create(context); 133 ((DocumentsApplication) context.getApplicationContext()).mUserManagerState = 134 userManagerState; 135 } 136 return userManagerState; 137 } 138 getDragAndDropManager(Context context)139 public static DragAndDropManager getDragAndDropManager(Context context) { 140 return ((DocumentsApplication) context.getApplicationContext()).mDragAndDropManager; 141 } 142 getFileTypeLookup(Context context)143 public static Lookup<String, String> getFileTypeLookup(Context context) { 144 return ((DocumentsApplication) context.getApplicationContext()).mFileTypeLookup; 145 } 146 147 /** 148 * Retrieve {@link ConfigStore} instance to access feature flags in production code. 149 */ getConfigStore()150 public static synchronized ConfigStore getConfigStore() { 151 if (sConfigStore == null) { 152 sConfigStore = new ConfigStore.ConfigStoreImpl(); 153 } 154 return sConfigStore; 155 } 156 157 /** 158 * Set {@link #mUserManagerState} as null onDestroy of BaseActivity so that new session uses new 159 * instance of {@link #mUserManagerState} 160 */ invalidateUserManagerState(Context context)161 public static void invalidateUserManagerState(Context context) { 162 ((DocumentsApplication) context.getApplicationContext()).mUserManagerState = null; 163 } 164 onApplyOverlayFinish(boolean result)165 private void onApplyOverlayFinish(boolean result) { 166 Log.d(TAG, "OverlayManager.setEnabled() result: " + result); 167 } 168 169 @SuppressLint("NewApi") // OverlayManager.class is @hide 170 @Override onCreate()171 public void onCreate() { 172 super.onCreate(); 173 synchronized (DocumentsApplication.class) { 174 if (sConfigStore == null) { 175 sConfigStore = new ConfigStore.ConfigStoreImpl(); 176 } 177 } 178 179 final ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); 180 final OverlayManager om = getSystemService(OverlayManager.class); 181 final int memoryClassBytes = am.getMemoryClass() * 1024 * 1024; 182 183 if (om != null) { 184 new ThemeOverlayManager(om, getPackageName()).applyOverlays(this, true, 185 this::onApplyOverlayFinish); 186 } else { 187 Log.w(TAG, "Can't obtain OverlayManager from System Service!"); 188 } 189 190 if (getConfigStore().isPrivateSpaceInDocsUIEnabled()) { 191 mUserManagerState = UserManagerState.create(this); 192 mUserIdManager = null; 193 } else { 194 mUserManagerState = null; 195 mUserIdManager = UserIdManager.create(this); 196 } 197 mProviders = new ProvidersCache(this); 198 199 mProviders.updateAsync(/* forceRefreshAll= */ false, /* callback= */ null); 200 201 mThumbnailCache = new ThumbnailCache(memoryClassBytes / 4); 202 203 mClipStore = new ClipStorage( 204 ClipStorage.prepareStorage(getCacheDir()), 205 getSharedPreferences(ClipStorage.PREF_NAME, 0)); 206 mClipper = DocumentClipper.create(this, mClipStore); 207 208 mDragAndDropManager = DragAndDropManager.create(this, mClipper); 209 210 mFileTypeLookup = new FileTypeMap(this); 211 212 final IntentFilter packageFilter = new IntentFilter(); 213 for (String packageAction : PACKAGE_FILTER_ACTIONS) { 214 packageFilter.addAction(packageAction); 215 } 216 packageFilter.addDataScheme("package"); 217 registerReceiver(mCacheReceiver, packageFilter); 218 219 final IntentFilter localeFilter = new IntentFilter(); 220 localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED); 221 registerReceiver(mCacheReceiver, localeFilter); 222 223 if (SdkLevel.isAtLeastV()) { 224 PROFILE_FILTER_ACTIONS.addAll(Lists.newArrayList( 225 Intent.ACTION_PROFILE_ADDED, 226 Intent.ACTION_PROFILE_REMOVED, 227 Intent.ACTION_PROFILE_AVAILABLE, 228 Intent.ACTION_PROFILE_UNAVAILABLE 229 )); 230 } 231 final IntentFilter profileFilter = new IntentFilter(); 232 for (String profileAction : PROFILE_FILTER_ACTIONS) { 233 profileFilter.addAction(profileAction); 234 } 235 registerReceiver(mCacheReceiver, profileFilter); 236 237 SearchHistoryManager.getInstance(getApplicationContext()); 238 } 239 240 @Override onTrimMemory(int level)241 public void onTrimMemory(int level) { 242 super.onTrimMemory(level); 243 244 mThumbnailCache.onTrimMemory(level); 245 } 246 247 private BroadcastReceiver mCacheReceiver = new BroadcastReceiver() { 248 @Override 249 public void onReceive(Context context, Intent intent) { 250 final Uri data = intent.getData(); 251 final String action = intent.getAction(); 252 if (PACKAGE_FILTER_ACTIONS.contains(action) && data != null) { 253 final String packageName = data.getSchemeSpecificPart(); 254 mProviders.updatePackageAsync(UserId.DEFAULT_USER, packageName); 255 } else if (PROFILE_FILTER_ACTIONS.contains(action)) { 256 // Make the changes to UserManagerState object before calling providers updateAsync 257 // so that providers for all the users are loaded 258 if (getConfigStore().isPrivateSpaceInDocsUIEnabled() && SdkLevel.isAtLeastV()) { 259 UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER); 260 UserId userId = UserId.of(userHandle); 261 getUserManagerState(context).onProfileActionStatusChange(action, userId); 262 } 263 // After we have reloaded roots. Resend the broadcast locally so the other 264 // components can reload properly after roots are updated. 265 mProviders.updateAsync(/* forceRefreshAll= */ true, 266 () -> LocalBroadcastManager.getInstance(context).sendBroadcast(intent)); 267 } else { 268 mProviders.updateAsync(/* forceRefreshAll= */ true, /* callback= */ null); 269 } 270 } 271 }; 272 } 273