blob: f4892b2ed9db4087a9f781806d2feecd41566e13 [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
2 * Copyright (C) 2008 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
Daniel Sandler325dc232013-06-05 22:57:57 -040017package com.android.launcher3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
Sunny Goyal3e58eea2022-11-14 14:30:07 -080019import static com.android.launcher3.DefaultLayoutParser.RES_PARTNER_DEFAULT_LAYOUT;
Tracy Zhouf6018722020-02-06 16:37:16 -080020import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
Sunny Goyal161a2142018-10-29 14:02:20 -070021import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
22
Sunny Goyal22ca9ec2017-05-18 15:03:13 -070023import android.annotation.TargetApi;
Mike Cleronb87bd162009-10-30 16:36:56 -070024import android.appwidget.AppWidgetManager;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080025import android.content.ComponentName;
Adam Cohen228da5a2011-07-27 22:23:47 -070026import android.content.ContentProvider;
Yura085c8532014-02-11 15:15:29 +000027import android.content.ContentProviderOperation;
28import android.content.ContentProviderResult;
Adam Cohen228da5a2011-07-27 22:23:47 -070029import android.content.ContentUris;
30import android.content.ContentValues;
31import android.content.Context;
Yura085c8532014-02-11 15:15:29 +000032import android.content.OperationApplicationException;
Michael Jurkab85f8a42012-04-25 15:48:32 -070033import android.content.SharedPreferences;
Sunny Goyalc0f03d92019-03-22 14:13:36 -070034import android.content.pm.ProviderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080035import android.database.Cursor;
36import android.database.SQLException;
Adam Cohen228da5a2011-07-27 22:23:47 -070037import android.database.sqlite.SQLiteDatabase;
Adam Cohen228da5a2011-07-27 22:23:47 -070038import android.database.sqlite.SQLiteQueryBuilder;
Adam Cohen228da5a2011-07-27 22:23:47 -070039import android.net.Uri;
Sunny Goyal7779d622015-06-11 16:18:39 -070040import android.os.Binder;
Sunny Goyal22ca9ec2017-05-18 15:03:13 -070041import android.os.Build;
Sunny Goyalb2d46ce2015-03-26 11:32:11 -070042import android.os.Bundle;
Sunny Goyal7779d622015-06-11 16:18:39 -070043import android.os.Process;
Sunny Goyal337c81f2019-12-10 12:19:13 -080044import android.os.UserManager;
Sunny Goyalc0f03d92019-03-22 14:13:36 -070045import android.provider.Settings;
Adam Cohen228da5a2011-07-27 22:23:47 -070046import android.text.TextUtils;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080047import android.util.Log;
Sunny Goyalc0f03d92019-03-22 14:13:36 -070048import android.util.Xml;
Adam Cohen228da5a2011-07-27 22:23:47 -070049
Sunny Goyal0fe505b2014-08-06 09:55:36 -070050import com.android.launcher3.LauncherSettings.Favorites;
Sunny Goyala9e2f5a2016-06-10 12:22:04 -070051import com.android.launcher3.config.FeatureFlags;
Sunny Goyalba279582023-04-07 11:45:09 -070052import com.android.launcher3.model.DatabaseHelper;
Sunny Goyala9e2f5a2016-06-10 12:22:04 -070053import com.android.launcher3.provider.LauncherDbUtils;
Sunny Goyaldbfc9012017-04-17 16:58:36 -070054import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
Sunny Goyale8f7d5a2016-05-24 11:30:14 -070055import com.android.launcher3.provider.RestoreDbTask;
Sunny Goyalc0f03d92019-03-22 14:13:36 -070056import com.android.launcher3.util.IOUtils;
Sunny Goyalefb7e842018-10-04 15:11:00 -070057import com.android.launcher3.util.IntArray;
Sunny Goyal3e58eea2022-11-14 14:30:07 -080058import com.android.launcher3.util.Partner;
Adam Cohen091440a2015-03-18 14:16:05 -070059import com.android.launcher3.util.Thunk;
Sihua Ma8bbfcb62022-11-08 16:46:07 -080060import com.android.launcher3.widget.LauncherWidgetHolder;
Michael Jurka8b805b12012-04-18 14:23:14 -070061
Sunny Goyalc0f03d92019-03-22 14:13:36 -070062import org.xmlpull.v1.XmlPullParser;
63
Hyunyoung Song6aa37292017-02-06 10:46:24 -080064import java.io.FileDescriptor;
Sunny Goyalc0f03d92019-03-22 14:13:36 -070065import java.io.InputStream;
Hyunyoung Song6aa37292017-02-06 10:46:24 -080066import java.io.PrintWriter;
Sunny Goyalc0f03d92019-03-22 14:13:36 -070067import java.io.StringReader;
Adam Cohen228da5a2011-07-27 22:23:47 -070068import java.util.ArrayList;
Tracy Zhouc0000452020-03-17 18:28:38 -070069import java.util.function.Supplier;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080070
The Android Open Source Project31dd5032009-03-03 19:32:27 -080071public class LauncherProvider extends ContentProvider {
Sunny Goyalc74e4192015-09-08 14:01:03 -070072 private static final String TAG = "LauncherProvider";
The Android Open Source Project31dd5032009-03-03 19:32:27 -080073
Sunny Goyal4276e7b2018-11-26 23:44:41 -080074 public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings";
Winson Chung3d503fb2011-07-13 17:25:49 -070075
Schneider Victor-tuliasfb252e72022-02-10 11:10:21 -080076 private static final int TEST_WORKSPACE_LAYOUT_RES_XML = R.xml.default_test_workspace;
Alex Chauf8b79d42022-12-15 13:41:49 +000077 private static final int TEST2_WORKSPACE_LAYOUT_RES_XML = R.xml.default_test2_workspace;
Sebastian Franco239ae0a2023-03-03 15:56:52 -080078 private static final int TAPL_WORKSPACE_LAYOUT_RES_XML = R.xml.default_tapl_test_workspace;
Schneider Victor-tuliasfb252e72022-02-10 11:10:21 -080079
Sunny Goyalba279582023-04-07 11:45:09 -070080 public static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
The Android Open Source Project31dd5032009-03-03 19:32:27 -080081
Sunny Goyalf076eae2016-01-11 12:25:10 -080082 protected DatabaseHelper mOpenHelper;
Pinyao Ting96186af2020-07-20 11:03:39 -070083 protected String mProviderAuthority;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080084
Alex Chauf8b79d42022-12-15 13:41:49 +000085 private int mDefaultWorkspaceLayoutOverride = 0;
Schneider Victor-tuliasfb252e72022-02-10 11:10:21 -080086
Hyunyoung Song6aa37292017-02-06 10:46:24 -080087 /**
88 * $ adb shell dumpsys activity provider com.android.launcher3
89 */
90 @Override
91 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
92 LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
93 if (appState == null || !appState.getModel().isModelLoaded()) {
94 return;
95 }
96 appState.getModel().dumpState("", fd, writer, args);
97 }
98
The Android Open Source Project31dd5032009-03-03 19:32:27 -080099 @Override
100 public boolean onCreate() {
Zak Cohen3eeb41d2020-02-14 14:15:13 -0800101 if (FeatureFlags.IS_STUDIO_BUILD) {
Sunny Goyale26d1002016-06-20 14:52:14 -0700102 Log.d(TAG, "Launcher process started");
103 }
Sunny Goyalb5b55c82016-05-10 12:28:59 -0700104
Sunny Goyalfdbef272017-02-01 12:52:54 -0800105 // The content provider exists for the entire duration of the launcher main process and
Sunny Goyal66f2b352018-02-09 10:57:12 -0800106 // is the first component to get created.
107 MainProcessInitializer.initialize(getContext().getApplicationContext());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800108 return true;
109 }
110
111 @Override
112 public String getType(Uri uri) {
113 SqlArguments args = new SqlArguments(uri, null, null);
114 if (TextUtils.isEmpty(args.where)) {
115 return "vnd.android.cursor.dir/" + args.table;
116 } else {
117 return "vnd.android.cursor.item/" + args.table;
118 }
119 }
120
Sunny Goyalf076eae2016-01-11 12:25:10 -0800121 /**
122 * Overridden in tests
123 */
124 protected synchronized void createDbIfNotExists() {
Sunny Goyald3849d12015-10-29 10:28:32 -0700125 if (mOpenHelper == null) {
Tracy Zhoued5f3082020-04-20 01:13:26 -0700126 mOpenHelper = DatabaseHelper.createDatabaseHelper(
127 getContext(), false /* forMigration */);
Sunny Goyale8f7d5a2016-05-24 11:30:14 -0700128
Sunny Goyal68031ca2021-08-02 12:23:44 -0700129 RestoreDbTask.restoreIfNeeded(getContext(), mOpenHelper);
Sunny Goyald3849d12015-10-29 10:28:32 -0700130 }
131 }
132
Tracy Zhouc0000452020-03-17 18:28:38 -0700133 private synchronized boolean prepForMigration(String dbFile, String targetTableName,
134 Supplier<DatabaseHelper> src, Supplier<DatabaseHelper> dst) {
135 if (TextUtils.equals(dbFile, mOpenHelper.getDatabaseName())) {
Alex Chau0adc3662022-07-04 13:05:38 +0100136 Log.e(TAG, "prepForMigration - target db is same as current: " + dbFile);
Tracy Zhou7df93d22020-01-27 13:44:06 -0800137 return false;
138 }
139
Tracy Zhouc0000452020-03-17 18:28:38 -0700140 final DatabaseHelper helper = src.get();
141 mOpenHelper = dst.get();
142 copyTable(helper.getReadableDatabase(), Favorites.TABLE_NAME,
143 mOpenHelper.getWritableDatabase(), targetTableName, getContext());
144 helper.close();
Tracy Zhou7df93d22020-01-27 13:44:06 -0800145 return true;
146 }
147
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800148 @Override
149 public Cursor query(Uri uri, String[] projection, String selection,
150 String[] selectionArgs, String sortOrder) {
Sunny Goyald3849d12015-10-29 10:28:32 -0700151 createDbIfNotExists();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800152
153 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
154 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
155 qb.setTables(args.table);
156
Romain Guy73b979d2009-06-09 12:57:21 -0700157 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800158 Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder);
Pinyao Ting0960b452020-10-14 11:17:04 -0700159 final Bundle extra = new Bundle();
160 extra.putString(LauncherSettings.Settings.EXTRA_DB_NAME, mOpenHelper.getDatabaseName());
161 result.setExtras(extra);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800162 result.setNotificationUri(getContext().getContentResolver(), uri);
163
164 return result;
165 }
166
Sunny Goyald1064182015-08-13 12:08:30 -0700167 private void reloadLauncherIfExternal() {
Sunny Goyal8c48d8b2019-01-25 15:10:18 -0800168 if (Binder.getCallingPid() != Process.myPid()) {
Sunny Goyald1064182015-08-13 12:08:30 -0700169 LauncherAppState app = LauncherAppState.getInstanceNoCreate();
170 if (app != null) {
Sunny Goyaldd96a5e2017-02-17 11:22:34 -0800171 app.getModel().forceReload();
Sunny Goyald1064182015-08-13 12:08:30 -0700172 }
173 }
174 }
175
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800176 @Override
177 public Uri insert(Uri uri, ContentValues initialValues) {
Sunny Goyald3849d12015-10-29 10:28:32 -0700178 createDbIfNotExists();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800179 SqlArguments args = new SqlArguments(uri);
180
Sunny Goyald1064182015-08-13 12:08:30 -0700181 // In very limited cases, we support system|signature permission apps to modify the db.
182 if (Binder.getCallingPid() != Process.myPid()) {
Sunny Goyalb5b55c82016-05-10 12:28:59 -0700183 if (!initializeExternalAdd(initialValues)) {
Adam Cohena043fa82014-07-23 14:49:38 -0700184 return null;
185 }
186 }
187
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800188 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Chris Wren1ada10d2013-09-13 18:01:38 -0400189 addModifiedTime(initialValues);
Sunny Goyalba279582023-04-07 11:45:09 -0700190 final int rowId = mOpenHelper.dbInsertAndCheck(db, args.table, initialValues);
Sunny Goyale72f3d52015-03-02 14:24:21 -0800191 if (rowId < 0) return null;
Tracy Zhou7df93d22020-01-27 13:44:06 -0800192 onAddOrDeleteOp(db);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800193
194 uri = ContentUris.withAppendedId(uri, rowId);
Sunny Goyal8c48d8b2019-01-25 15:10:18 -0800195 reloadLauncherIfExternal();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800196 return uri;
197 }
198
Sunny Goyalb5b55c82016-05-10 12:28:59 -0700199 private boolean initializeExternalAdd(ContentValues values) {
200 // 1. Ensure that externally added items have a valid item id
Sunny Goyalefb7e842018-10-04 15:11:00 -0700201 int id = mOpenHelper.generateNewItemId();
Sunny Goyalb5b55c82016-05-10 12:28:59 -0700202 values.put(LauncherSettings.Favorites._ID, id);
203
204 // 2. In the case of an app widget, and if no app widget id is specified, we
205 // attempt allocate and bind the widget.
206 Integer itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE);
207 if (itemType != null &&
208 itemType.intValue() == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET &&
209 !values.containsKey(LauncherSettings.Favorites.APPWIDGET_ID)) {
210
211 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getContext());
212 ComponentName cn = ComponentName.unflattenFromString(
213 values.getAsString(Favorites.APPWIDGET_PROVIDER));
214
215 if (cn != null) {
Sihua Maaa2b8722022-10-25 15:17:58 -0700216 LauncherWidgetHolder widgetHolder = mOpenHelper.newLauncherWidgetHolder();
Sunny Goyalb5b55c82016-05-10 12:28:59 -0700217 try {
Sihua Maaa2b8722022-10-25 15:17:58 -0700218 int appWidgetId = widgetHolder.allocateAppWidgetId();
Sunny Goyalb5b55c82016-05-10 12:28:59 -0700219 values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
220 if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,cn)) {
Sihua Maaa2b8722022-10-25 15:17:58 -0700221 widgetHolder.deleteAppWidgetId(appWidgetId);
Sunny Goyalb5b55c82016-05-10 12:28:59 -0700222 return false;
223 }
224 } catch (RuntimeException e) {
225 Log.e(TAG, "Failed to initialize external widget", e);
226 return false;
Sihua Maaa2b8722022-10-25 15:17:58 -0700227 } finally {
228 // Necessary to destroy the holder to free up possible activity context
229 widgetHolder.destroy();
Sunny Goyalb5b55c82016-05-10 12:28:59 -0700230 }
231 } else {
232 return false;
233 }
234 }
235
Sunny Goyalc5939392018-12-07 11:43:47 -0800236 return true;
Sunny Goyalb5b55c82016-05-10 12:28:59 -0700237 }
238
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800239 @Override
240 public int bulkInsert(Uri uri, ContentValues[] values) {
Sunny Goyald3849d12015-10-29 10:28:32 -0700241 createDbIfNotExists();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800242 SqlArguments args = new SqlArguments(uri);
243
244 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Sunny Goyaldbfc9012017-04-17 16:58:36 -0700245 try (SQLiteTransaction t = new SQLiteTransaction(db)) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800246 int numValues = values.length;
247 for (int i = 0; i < numValues; i++) {
Chris Wren1ada10d2013-09-13 18:01:38 -0400248 addModifiedTime(values[i]);
Sunny Goyalba279582023-04-07 11:45:09 -0700249 if (mOpenHelper.dbInsertAndCheck(db, args.table, values[i]) < 0) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700250 return 0;
251 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800252 }
Tracy Zhou7df93d22020-01-27 13:44:06 -0800253 onAddOrDeleteOp(db);
Sunny Goyaldbfc9012017-04-17 16:58:36 -0700254 t.commit();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800255 }
256
Sunny Goyald1064182015-08-13 12:08:30 -0700257 reloadLauncherIfExternal();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800258 return values.length;
259 }
260
Sunny Goyal161a2142018-10-29 14:02:20 -0700261 @TargetApi(Build.VERSION_CODES.M)
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800262 @Override
Yura085c8532014-02-11 15:15:29 +0000263 public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
264 throws OperationApplicationException {
Sunny Goyald3849d12015-10-29 10:28:32 -0700265 createDbIfNotExists();
Sunny Goyaldbfc9012017-04-17 16:58:36 -0700266 try (SQLiteTransaction t = new SQLiteTransaction(mOpenHelper.getWritableDatabase())) {
Sunny Goyal8c48d8b2019-01-25 15:10:18 -0800267 boolean isAddOrDelete = false;
Sunny Goyal161a2142018-10-29 14:02:20 -0700268
269 final int numOperations = operations.size();
270 final ContentProviderResult[] results = new ContentProviderResult[numOperations];
271 for (int i = 0; i < numOperations; i++) {
272 ContentProviderOperation op = operations.get(i);
273 results[i] = op.apply(this, results, i);
274
275 isAddOrDelete |= (op.isInsert() || op.isDelete()) &&
276 results[i].count != null && results[i].count > 0;
277 }
278 if (isAddOrDelete) {
Tracy Zhou7df93d22020-01-27 13:44:06 -0800279 onAddOrDeleteOp(t.getDb());
Sunny Goyal161a2142018-10-29 14:02:20 -0700280 }
281
Sunny Goyaldbfc9012017-04-17 16:58:36 -0700282 t.commit();
Sunny Goyald1064182015-08-13 12:08:30 -0700283 reloadLauncherIfExternal();
Sunny Goyal161a2142018-10-29 14:02:20 -0700284 return results;
Yura085c8532014-02-11 15:15:29 +0000285 }
286 }
287
288 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800289 public int delete(Uri uri, String selection, String[] selectionArgs) {
Sunny Goyald3849d12015-10-29 10:28:32 -0700290 createDbIfNotExists();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800291 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
292
293 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800294
Louis Begina9c21c62016-08-15 15:18:41 -0700295 if (Binder.getCallingPid() != Process.myPid()
296 && Favorites.TABLE_NAME.equalsIgnoreCase(args.table)) {
Sunny Goyal55fddc82017-04-06 15:02:52 -0700297 mOpenHelper.removeGhostWidgets(mOpenHelper.getWritableDatabase());
Louis Begina9c21c62016-08-15 15:18:41 -0700298 }
299 int count = db.delete(args.table, args.where, args.args);
300 if (count > 0) {
Tracy Zhou7df93d22020-01-27 13:44:06 -0800301 onAddOrDeleteOp(db);
Louis Begina9c21c62016-08-15 15:18:41 -0700302 reloadLauncherIfExternal();
303 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800304 return count;
305 }
306
307 @Override
308 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
Sunny Goyald3849d12015-10-29 10:28:32 -0700309 createDbIfNotExists();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800310 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
311
Chris Wren1ada10d2013-09-13 18:01:38 -0400312 addModifiedTime(values);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800313 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
314 int count = db.update(args.table, values, args.where, args.args);
Sunny Goyald1064182015-08-13 12:08:30 -0700315 reloadLauncherIfExternal();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800316 return count;
317 }
318
Sunny Goyal7779d622015-06-11 16:18:39 -0700319 @Override
Tony Wickham827cef22016-03-17 15:39:39 -0700320 public Bundle call(String method, final String arg, final Bundle extras) {
Sunny Goyal7779d622015-06-11 16:18:39 -0700321 if (Binder.getCallingUid() != Process.myUid()) {
322 return null;
323 }
Sunny Goyald3849d12015-10-29 10:28:32 -0700324 createDbIfNotExists();
Sunny Goyal7779d622015-06-11 16:18:39 -0700325
326 switch (method) {
Sunny Goyald2497482015-09-22 18:24:19 -0700327 case LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG: {
328 clearFlagEmptyDbCreated();
329 return null;
330 }
331 case LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS: {
332 Bundle result = new Bundle();
Sunny Goyalefb7e842018-10-04 15:11:00 -0700333 result.putIntArray(LauncherSettings.Settings.EXTRA_VALUE, deleteEmptyFolders()
334 .toArray());
Sunny Goyald2497482015-09-22 18:24:19 -0700335 return result;
336 }
337 case LauncherSettings.Settings.METHOD_NEW_ITEM_ID: {
338 Bundle result = new Bundle();
Tracy Zhou7df93d22020-01-27 13:44:06 -0800339 result.putInt(LauncherSettings.Settings.EXTRA_VALUE,
340 mOpenHelper.generateNewItemId());
Sunny Goyald2497482015-09-22 18:24:19 -0700341 return result;
342 }
343 case LauncherSettings.Settings.METHOD_NEW_SCREEN_ID: {
344 Bundle result = new Bundle();
Tracy Zhou7df93d22020-01-27 13:44:06 -0800345 result.putInt(LauncherSettings.Settings.EXTRA_VALUE,
Andras Kloczl953eb802021-10-15 21:16:48 +0100346 mOpenHelper.getNewScreenId());
Sunny Goyald2497482015-09-22 18:24:19 -0700347 return result;
348 }
349 case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: {
Sunny Goyale05b08f2017-02-23 18:30:22 -0800350 mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
Sunny Goyald2497482015-09-22 18:24:19 -0700351 return null;
352 }
Schneider Victor-tuliasfb252e72022-02-10 11:10:21 -0800353 case LauncherSettings.Settings.METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG: {
Alex Chauf8b79d42022-12-15 13:41:49 +0000354 switch (arg) {
355 case LauncherSettings.Settings.ARG_DEFAULT_WORKSPACE_LAYOUT_TEST:
356 mDefaultWorkspaceLayoutOverride = TEST_WORKSPACE_LAYOUT_RES_XML;
357 break;
358 case LauncherSettings.Settings.ARG_DEFAULT_WORKSPACE_LAYOUT_TEST2:
359 mDefaultWorkspaceLayoutOverride = TEST2_WORKSPACE_LAYOUT_RES_XML;
360 break;
Sebastian Franco239ae0a2023-03-03 15:56:52 -0800361 case LauncherSettings.Settings.ARG_DEFAULT_WORKSPACE_LAYOUT_TAPL:
362 mDefaultWorkspaceLayoutOverride = TAPL_WORKSPACE_LAYOUT_RES_XML;
363 break;
Alex Chauf8b79d42022-12-15 13:41:49 +0000364 default:
365 mDefaultWorkspaceLayoutOverride = 0;
366 break;
367 }
Schneider Victor-tuliasfb252e72022-02-10 11:10:21 -0800368 return null;
369 }
370 case LauncherSettings.Settings.METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG: {
Alex Chauf8b79d42022-12-15 13:41:49 +0000371 mDefaultWorkspaceLayoutOverride = 0;
Schneider Victor-tuliasfb252e72022-02-10 11:10:21 -0800372 return null;
373 }
Sunny Goyald2497482015-09-22 18:24:19 -0700374 case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: {
375 loadDefaultFavoritesIfNecessary();
376 return null;
377 }
Sunny Goyal55fddc82017-04-06 15:02:52 -0700378 case LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS: {
379 mOpenHelper.removeGhostWidgets(mOpenHelper.getWritableDatabase());
380 return null;
381 }
Sunny Goyal161a2142018-10-29 14:02:20 -0700382 case LauncherSettings.Settings.METHOD_NEW_TRANSACTION: {
383 Bundle result = new Bundle();
384 result.putBinder(LauncherSettings.Settings.EXTRA_VALUE,
385 new SQLiteTransaction(mOpenHelper.getWritableDatabase()));
386 return result;
387 }
Samuel Fufaf667a132020-05-29 14:47:42 -0700388 case LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE: {
389 mOpenHelper.mHotseatRestoreTableExists = tableExists(
390 mOpenHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE);
391 return null;
392 }
Tracy Zhou7df93d22020-01-27 13:44:06 -0800393 case LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER: {
Sunny Goyal278939f2021-07-28 14:35:47 -0700394 Bundle result = new Bundle();
395 result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
396 prepForMigration(
Alex Chaufe9a07b2022-03-08 21:48:34 +0000397 arg /* dbFile */,
Sunny Goyal278939f2021-07-28 14:35:47 -0700398 Favorites.TMP_TABLE,
399 () -> mOpenHelper,
400 () -> DatabaseHelper.createDatabaseHelper(
401 getContext(), true /* forMigration */)));
402 return result;
Tracy Zhouc0000452020-03-17 18:28:38 -0700403 }
404 case LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW: {
Sunny Goyal278939f2021-07-28 14:35:47 -0700405 Bundle result = new Bundle();
406 result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
407 prepForMigration(
408 arg /* dbFile */,
409 Favorites.PREVIEW_TABLE_NAME,
410 () -> DatabaseHelper.createDatabaseHelper(
411 getContext(), arg, true /* forMigration */),
412 () -> mOpenHelper));
413 return result;
Pinyao Ting96186af2020-07-20 11:03:39 -0700414 }
Sunny Goyal7779d622015-06-11 16:18:39 -0700415 }
416 return null;
417 }
418
Tracy Zhou7df93d22020-01-27 13:44:06 -0800419 private void onAddOrDeleteOp(SQLiteDatabase db) {
Pinyao Ting26c4e232020-04-15 22:25:06 -0700420 mOpenHelper.onAddOrDeleteOp(db);
Tracy Zhou7df93d22020-01-27 13:44:06 -0800421 }
422
Sunny Goyalb1622cc2015-06-10 16:00:42 -0700423 /**
424 * Deletes any empty folder from the DB.
425 * @return Ids of deleted folders.
426 */
Sunny Goyalefb7e842018-10-04 15:11:00 -0700427 private IntArray deleteEmptyFolders() {
Sunny Goyalb1622cc2015-06-10 16:00:42 -0700428 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Sunny Goyaldbfc9012017-04-17 16:58:36 -0700429 try (SQLiteTransaction t = new SQLiteTransaction(db)) {
Sunny Goyalb1622cc2015-06-10 16:00:42 -0700430 // Select folders whose id do not match any container value.
431 String selection = LauncherSettings.Favorites.ITEM_TYPE + " = "
432 + LauncherSettings.Favorites.ITEM_TYPE_FOLDER + " AND "
433 + LauncherSettings.Favorites._ID + " NOT IN (SELECT " +
434 LauncherSettings.Favorites.CONTAINER + " FROM "
Sunny Goyalb5b55c82016-05-10 12:28:59 -0700435 + Favorites.TABLE_NAME + ")";
Sunny Goyalc5939392018-12-07 11:43:47 -0800436
Alex Chauac038432021-11-04 16:49:59 +0000437 IntArray folderIds = LauncherDbUtils.queryIntArray(false, db, Favorites.TABLE_NAME,
Sunny Goyalc5939392018-12-07 11:43:47 -0800438 Favorites._ID, selection, null, null);
Sunny Goyald2497482015-09-22 18:24:19 -0700439 if (!folderIds.isEmpty()) {
Sunny Goyalb5b55c82016-05-10 12:28:59 -0700440 db.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
Sunny Goyalb1622cc2015-06-10 16:00:42 -0700441 LauncherSettings.Favorites._ID, folderIds), null);
442 }
Sunny Goyaldbfc9012017-04-17 16:58:36 -0700443 t.commit();
Sunny Goyalc5939392018-12-07 11:43:47 -0800444 return folderIds;
Sunny Goyalb1622cc2015-06-10 16:00:42 -0700445 } catch (SQLException ex) {
446 Log.e(TAG, ex.getMessage(), ex);
Sunny Goyalc5939392018-12-07 11:43:47 -0800447 return new IntArray();
Sunny Goyalb1622cc2015-06-10 16:00:42 -0700448 }
Sunny Goyalb1622cc2015-06-10 16:00:42 -0700449 }
450
Adam Cohen091440a2015-03-18 14:16:05 -0700451 @Thunk static void addModifiedTime(ContentValues values) {
Sunny Goyalc5939392018-12-07 11:43:47 -0800452 values.put(LauncherSettings.Favorites.MODIFIED, System.currentTimeMillis());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800453 }
454
Sunny Goyald2497482015-09-22 18:24:19 -0700455 private void clearFlagEmptyDbCreated() {
Stefan Andonian146701c2022-11-10 23:07:40 +0000456 LauncherPrefs.getPrefs(getContext()).edit()
Pinyao Ting96186af2020-07-20 11:03:39 -0700457 .remove(mOpenHelper.getKey(EMPTY_DATABASE_CREATED)).commit();
Sunny Goyal33d44382014-10-16 09:24:19 -0700458 }
459
Sunny Goyal42de82f2014-09-26 22:09:29 -0700460 /**
Sunny Goyal0fe505b2014-08-06 09:55:36 -0700461 * Loads the default workspace based on the following priority scheme:
Sunny Goyalb2d46ce2015-03-26 11:32:11 -0700462 * 1) From the app restrictions
463 * 2) From a package provided by play store
464 * 3) From a partner configuration APK, already in the system image
465 * 4) The default configuration for the particular device
Brian Muramatsu5524b492012-10-02 16:55:54 -0700466 */
Sunny Goyald2497482015-09-22 18:24:19 -0700467 synchronized private void loadDefaultFavoritesIfNecessary() {
Stefan Andonian146701c2022-11-10 23:07:40 +0000468 SharedPreferences sp = LauncherPrefs.getPrefs(getContext());
Adam Cohene25af792013-06-06 23:08:25 -0700469
Pinyao Ting96186af2020-07-20 11:03:39 -0700470 if (sp.getBoolean(mOpenHelper.getKey(EMPTY_DATABASE_CREATED), false)) {
Chris Wren5dee7af2013-12-20 17:22:11 -0500471 Log.d(TAG, "loading default workspace");
Michael Jurka45355c42012-10-08 13:21:35 +0200472
Sihua Maaa2b8722022-10-25 15:17:58 -0700473 LauncherWidgetHolder widgetHolder = mOpenHelper.newLauncherWidgetHolder();
Sihua Macdb3c0a2023-02-13 15:12:32 -0800474 try {
475 AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction(widgetHolder);
476 if (loader == null) {
477 loader = AutoInstallsLayout.get(getContext(), widgetHolder, mOpenHelper);
478 }
479 if (loader == null) {
480 final Partner partner = Partner.get(getContext().getPackageManager());
481 if (partner != null) {
482 int workspaceResId = partner.getXmlResId(RES_PARTNER_DEFAULT_LAYOUT);
483 if (workspaceResId != 0) {
484 loader = new DefaultLayoutParser(getContext(), widgetHolder,
485 mOpenHelper, partner.getResources(), workspaceResId);
486 }
Sunny Goyal0fe505b2014-08-06 09:55:36 -0700487 }
Adam Cohen9b8f51f2014-05-30 15:34:09 -0700488 }
Sunny Goyal0fe505b2014-08-06 09:55:36 -0700489
Sihua Macdb3c0a2023-02-13 15:12:32 -0800490 final boolean usingExternallyProvidedLayout = loader != null;
491 if (loader == null) {
492 loader = getDefaultLayoutParser(widgetHolder);
493 }
Sunny Goyalc6c8fef2015-03-04 09:51:18 -0800494
Sihua Macdb3c0a2023-02-13 15:12:32 -0800495 // There might be some partially restored DB items, due to buggy restore logic in
496 // previous versions of launcher.
Sunny Goyale05b08f2017-02-23 18:30:22 -0800497 mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
Sihua Macdb3c0a2023-02-13 15:12:32 -0800498 // Populate favorites table with initial favorites
499 if ((mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), loader) <= 0)
500 && usingExternallyProvidedLayout) {
501 // Unable to load external layout. Cleanup and load the internal layout.
502 mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
503 mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(),
504 getDefaultLayoutParser(widgetHolder));
505 }
506 clearFlagEmptyDbCreated();
507 } finally {
508 widgetHolder.destroy();
Sunny Goyal9d219682014-10-23 14:21:02 -0700509 }
Michael Jurkab85f8a42012-04-25 15:48:32 -0700510 }
511 }
512
Sunny Goyalb2d46ce2015-03-26 11:32:11 -0700513 /**
514 * Creates workspace loader from an XML resource listed in the app restrictions.
515 *
516 * @return the loader if the restrictions are set and the resource exists; null otherwise.
517 */
Sihua Maaa2b8722022-10-25 15:17:58 -0700518 private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(
519 LauncherWidgetHolder widgetHolder) {
Sunny Goyalb2d46ce2015-03-26 11:32:11 -0700520 Context ctx = getContext();
Pinyao Ting96186af2020-07-20 11:03:39 -0700521 final String authority;
522 if (!TextUtils.isEmpty(mProviderAuthority)) {
523 authority = mProviderAuthority;
524 } else {
525 authority = Settings.Secure.getString(ctx.getContentResolver(),
526 "launcher3.layout.provider");
527 }
Sunny Goyalc0f03d92019-03-22 14:13:36 -0700528 if (TextUtils.isEmpty(authority)) {
Sunny Goyal35ca8732015-04-06 10:45:31 -0700529 return null;
530 }
Sunny Goyalb2d46ce2015-03-26 11:32:11 -0700531
Sunny Goyalc0f03d92019-03-22 14:13:36 -0700532 ProviderInfo pi = ctx.getPackageManager().resolveContentProvider(authority, 0);
533 if (pi == null) {
534 Log.e(TAG, "No provider found for authority " + authority);
535 return null;
Sunny Goyalb2d46ce2015-03-26 11:32:11 -0700536 }
Sunny Goyal4b5b0eb2019-12-02 17:00:35 -0800537 Uri uri = getLayoutUri(authority, ctx);
Sunny Goyalc0f03d92019-03-22 14:13:36 -0700538 try (InputStream in = ctx.getContentResolver().openInputStream(uri)) {
539 // Read the full xml so that we fail early in case of any IO error.
540 String layout = new String(IOUtils.toByteArray(in));
541 XmlPullParser parser = Xml.newPullParser();
542 parser.setInput(new StringReader(layout));
543
544 Log.d(TAG, "Loading layout from " + authority);
Sihua Maaa2b8722022-10-25 15:17:58 -0700545 return new AutoInstallsLayout(ctx, widgetHolder, mOpenHelper,
Sunny Goyalc0f03d92019-03-22 14:13:36 -0700546 ctx.getPackageManager().getResourcesForApplication(pi.applicationInfo),
547 () -> parser, AutoInstallsLayout.TAG_WORKSPACE);
548 } catch (Exception e) {
549 Log.e(TAG, "Error getting layout stream from: " + authority , e);
550 return null;
551 }
Sunny Goyalb2d46ce2015-03-26 11:32:11 -0700552 }
553
Sunny Goyal4b5b0eb2019-12-02 17:00:35 -0800554 public static Uri getLayoutUri(String authority, Context ctx) {
555 InvariantDeviceProfile grid = LauncherAppState.getIDP(ctx);
556 return new Uri.Builder().scheme("content").authority(authority).path("launcher_layout")
557 .appendQueryParameter("version", "1")
558 .appendQueryParameter("gridWidth", Integer.toString(grid.numColumns))
559 .appendQueryParameter("gridHeight", Integer.toString(grid.numRows))
Tony Wickhamb87f3cd2021-04-07 15:02:37 -0700560 .appendQueryParameter("hotseatSize", Integer.toString(grid.numDatabaseHotseatIcons))
Sunny Goyal4b5b0eb2019-12-02 17:00:35 -0800561 .build();
562 }
563
Sihua Maaa2b8722022-10-25 15:17:58 -0700564 private DefaultLayoutParser getDefaultLayoutParser(LauncherWidgetHolder widgetHolder) {
Adam Cohen27824492017-09-22 17:10:55 -0700565 InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
Alex Chauf8b79d42022-12-15 13:41:49 +0000566 int defaultLayout = mDefaultWorkspaceLayoutOverride > 0
567 ? mDefaultWorkspaceLayoutOverride : idp.defaultLayoutId;
Adam Cohen27824492017-09-22 17:10:55 -0700568
Sunny Goyal337c81f2019-12-10 12:19:13 -0800569 if (getContext().getSystemService(UserManager.class).isDemoUser()
570 && idp.demoModeLayoutId != 0) {
Adam Cohen27824492017-09-22 17:10:55 -0700571 defaultLayout = idp.demoModeLayoutId;
572 }
573
Sihua Maaa2b8722022-10-25 15:17:58 -0700574 return new DefaultLayoutParser(getContext(), widgetHolder,
Sunny Goyal9d219682014-10-23 14:21:02 -0700575 mOpenHelper, getContext().getResources(), defaultLayout);
576 }
577
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800578 static class SqlArguments {
579 public final String table;
580 public final String where;
581 public final String[] args;
582
583 SqlArguments(Uri url, String where, String[] args) {
584 if (url.getPathSegments().size() == 1) {
585 this.table = url.getPathSegments().get(0);
586 this.where = where;
587 this.args = args;
588 } else if (url.getPathSegments().size() != 2) {
589 throw new IllegalArgumentException("Invalid URI: " + url);
590 } else if (!TextUtils.isEmpty(where)) {
591 throw new UnsupportedOperationException("WHERE clause not supported: " + url);
592 } else {
593 this.table = url.getPathSegments().get(0);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700594 this.where = "_id=" + ContentUris.parseId(url);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800595 this.args = null;
596 }
597 }
598
599 SqlArguments(Uri url) {
600 if (url.getPathSegments().size() == 1) {
601 table = url.getPathSegments().get(0);
602 where = null;
603 args = null;
604 } else {
605 throw new IllegalArgumentException("Invalid URI: " + url);
606 }
607 }
608 }
Adam Cohen72960972014-01-15 18:13:55 -0800609}