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.Context; 21 import android.content.Intent; 22 import android.content.SharedPreferences; 23 import android.provider.Column; 24 import android.util.Log; 25 26 import com.android.providers.media.util.ForegroundThread; 27 import com.android.providers.media.util.Metrics; 28 29 import java.io.File; 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 File dbDir = context.getDatabasePath("foo").getParentFile(); 68 String[] files = dbDir.list(); 69 if (files == null) return; 70 for (int i=0; i<files.length; i++) { 71 String file = files[i]; 72 if (MediaProvider.isMediaDatabaseName(file)) { 73 long startTime = System.currentTimeMillis(); 74 Log.i(TAG, "---> Start upgrade of media database " + file); 75 try { 76 DatabaseHelper helper = new DatabaseHelper( 77 context, file, MediaProvider.isInternalMediaDatabaseName(file), 78 false, false, Column.class, Metrics::logSchemaChange, null, 79 MediaProvider.MIGRATION_LISTENER, null); 80 helper.runWithTransaction((db) -> { 81 // Perform just enough to force database upgrade 82 return db.getVersion(); 83 }); 84 helper.close(); 85 } catch (Throwable t) { 86 Log.wtf(TAG, "Error during upgrade of media db " + file, t); 87 } finally { 88 } 89 Log.i(TAG, "<--- Finished upgrade of media database " + file 90 + " in " + (System.currentTimeMillis()-startTime) + "ms"); 91 } 92 } 93 } catch (Throwable t) { 94 // Something has gone terribly wrong. 95 Log.wtf(TAG, "Error during upgrade attempt.", t); 96 } 97 } 98 } 99