1 /*
2  * Copyright (C) 2011 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.providers.media;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.ContentProviderClient;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.SharedPreferences;
24 import android.provider.MediaStore;
25 import android.util.Log;
26 
27 import com.android.providers.media.util.ForegroundThread;
28 
29 import java.util.Optional;
30 
31 /**
32  * This will be launched during system boot, after the core system has
33  * been brought up but before any non-persistent processes have been
34  * started.  It is launched in a special state, with no content provider
35  * or custom application class associated with the process running.
36  *
37  * It's job is to prime the contacts database. Either create it
38  * if it doesn't exist, or open it and force any necessary upgrades.
39  * All of this heavy lifting happens before the boot animation ends.
40  */
41 public class MediaUpgradeReceiver extends BroadcastReceiver {
42     static final String TAG = "MediaUpgradeReceiver";
43     static final String PREF_DB_VERSION = "db_version";
44 
45     @Override
onReceive(Context context, Intent intent)46     public void onReceive(Context context, Intent intent) {
47         // We are now running with the system up, but no apps started,
48         // so can do whatever cleanup after an upgrade that we want.
49         ForegroundThread.getExecutor().execute(() -> {
50             // Run database migration on a separate thread so that main thread
51             // is available for handling other MediaService requests.
52             tryMigratingDatabases(context);
53         });
54     }
55 
tryMigratingDatabases(Context context)56     private void tryMigratingDatabases(Context context) {
57         // Lookup the last known database version
58         SharedPreferences prefs = context.getSharedPreferences(TAG, Context.MODE_PRIVATE);
59         int prefVersion = prefs.getInt(PREF_DB_VERSION, 0);
60         int dbVersion = DatabaseHelper.getDatabaseVersion(context);
61         if (prefVersion == dbVersion) {
62             return;
63         }
64         prefs.edit().putInt(PREF_DB_VERSION, dbVersion).commit();
65 
66         try {
67             String[] files = context.databaseList();
68             if (files == null) return;
69 
70             MediaProvider mediaProvider = getMediaProvider(context);
71             for (String file : files) {
72                 Optional<DatabaseHelper> helper = mediaProvider.getDatabaseHelper(file);
73                 if (helper.isPresent()) {
74                     long startTime = System.currentTimeMillis();
75                     Log.i(TAG, "---> Start upgrade of media database " + file);
76                     try {
77                         helper.get().runWithTransaction((db) -> {
78                             // Perform just enough to force database upgrade
79                             return db.getVersion();
80                         });
81                     } catch (Throwable t) {
82                         Log.wtf(TAG, "Error during upgrade of media db " + file, t);
83                     }
84 
85                     Log.i(TAG, "<--- Finished upgrade of media database " + file
86                             + " in " + (System.currentTimeMillis() - startTime) + "ms");
87                 }
88             }
89         } catch (Throwable t) {
90             // Something has gone terribly wrong.
91             Log.wtf(TAG, "Error during upgrade attempt.", t);
92         }
93     }
94 
getMediaProvider(Context context)95     private MediaProvider getMediaProvider(Context context) {
96         try (ContentProviderClient cpc =
97                      context.getContentResolver().acquireContentProviderClient(
98                              MediaStore.AUTHORITY)) {
99             return (MediaProvider) cpc.getLocalContentProvider();
100         } catch (Exception e) {
101             throw new IllegalStateException("Failed to acquire MediaProvider", e);
102         }
103     }
104 }
105