1 package com.android.launcher3; 2 3 import static android.os.Process.myUserHandle; 4 5 import android.appwidget.AppWidgetHost; 6 import android.appwidget.AppWidgetManager; 7 import android.appwidget.AppWidgetProviderInfo; 8 import android.content.BroadcastReceiver; 9 import android.content.ContentResolver; 10 import android.content.Context; 11 import android.content.Intent; 12 import android.database.Cursor; 13 import android.util.Log; 14 15 import androidx.annotation.WorkerThread; 16 17 import com.android.launcher3.LauncherSettings.Favorites; 18 import com.android.launcher3.model.LoaderTask; 19 import com.android.launcher3.model.WidgetsModel; 20 import com.android.launcher3.model.data.LauncherAppWidgetInfo; 21 import com.android.launcher3.pm.UserCache; 22 import com.android.launcher3.provider.RestoreDbTask; 23 import com.android.launcher3.util.ContentWriter; 24 import com.android.launcher3.widget.LauncherAppWidgetHost; 25 26 public class AppWidgetsRestoredReceiver extends BroadcastReceiver { 27 28 private static final String TAG = "AWRestoredReceiver"; 29 30 @Override onReceive(final Context context, Intent intent)31 public void onReceive(final Context context, Intent intent) { 32 if (AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED.equals(intent.getAction())) { 33 int hostId = intent.getIntExtra(AppWidgetManager.EXTRA_HOST_ID, 0); 34 Log.d(TAG, "Widget ID map received for host:" + hostId); 35 if (hostId != LauncherAppWidgetHost.APPWIDGET_HOST_ID) { 36 return; 37 } 38 39 final int[] oldIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS); 40 final int[] newIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); 41 if (oldIds != null && newIds != null && oldIds.length == newIds.length) { 42 RestoreDbTask.setRestoredAppWidgetIds(context, oldIds, newIds); 43 } else { 44 Log.e(TAG, "Invalid host restored received"); 45 } 46 } 47 } 48 49 /** 50 * Updates the app widgets whose id has changed during the restore process. 51 */ 52 @WorkerThread restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds)53 public static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds) { 54 AppWidgetHost appWidgetHost = new LauncherAppWidgetHost(context); 55 if (WidgetsModel.GO_DISABLE_WIDGETS) { 56 Log.e(TAG, "Skipping widget ID remap as widgets not supported"); 57 appWidgetHost.deleteHost(); 58 return; 59 } 60 if (!RestoreDbTask.isPending(context)) { 61 // Someone has already gone through our DB once, probably LoaderTask. Skip any further 62 // modifications of the DB. 63 Log.e(TAG, "Skipping widget ID remap as DB already in use"); 64 for (int widgetId : newWidgetIds) { 65 Log.d(TAG, "Deleting widgetId: " + widgetId); 66 appWidgetHost.deleteAppWidgetId(widgetId); 67 } 68 return; 69 } 70 final ContentResolver cr = context.getContentResolver(); 71 final AppWidgetManager widgets = AppWidgetManager.getInstance(context); 72 73 for (int i = 0; i < oldWidgetIds.length; i++) { 74 Log.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]); 75 76 final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]); 77 final int state; 78 if (LoaderTask.isValidProvider(provider)) { 79 // This will ensure that we show 'Click to setup' UI if required. 80 state = LauncherAppWidgetInfo.FLAG_UI_NOT_READY; 81 } else { 82 state = LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; 83 } 84 85 // b/135926478: Work profile widget restore is broken in platform. This forces us to 86 // recreate the widget during loading with the correct host provider. 87 long mainProfileId = UserCache.INSTANCE.get(context) 88 .getSerialNumberForUser(myUserHandle()); 89 String oldWidgetId = Integer.toString(oldWidgetIds[i]); 90 final String where = "appWidgetId=? and (restored & 1) = 1 and profileId=?"; 91 final String[] args = new String[] { oldWidgetId, Long.toString(mainProfileId) }; 92 int result = new ContentWriter(context, new ContentWriter.CommitParams(where, args)) 93 .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i]) 94 .put(LauncherSettings.Favorites.RESTORED, state) 95 .commit(); 96 if (result == 0) { 97 Cursor cursor = cr.query(Favorites.CONTENT_URI, 98 new String[] {Favorites.APPWIDGET_ID}, 99 "appWidgetId=?", new String[] { oldWidgetId }, null); 100 try { 101 if (!cursor.moveToFirst()) { 102 // The widget no long exists. 103 appWidgetHost.deleteAppWidgetId(newWidgetIds[i]); 104 } 105 } finally { 106 cursor.close(); 107 } 108 } 109 // attempt to update widget id in backup table as well 110 new ContentWriter(context, ContentWriter.CommitParams.backupCommitParams( 111 "appWidgetId=? and profileId=?", args)) 112 .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i]) 113 .put(LauncherSettings.Favorites.RESTORED, state) 114 .commit(); 115 } 116 117 LauncherAppState app = LauncherAppState.getInstanceNoCreate(); 118 if (app != null) { 119 app.getModel().forceReload(); 120 } 121 } 122 } 123