Merge "Implement extra persistence layer for widget provider info" into tm-dev am: a710d8fe63 am: 9bb639e53b
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17355277 Change-Id: I356e5239e1e1d86d153999b9f2052dab52f776e1 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -536,6 +536,12 @@ public final class SystemUiDeviceConfigFlags {
|
||||
*/
|
||||
public static final String CLIPBOARD_OVERLAY_ENABLED = "clipboard_overlay_enabled";
|
||||
|
||||
/**
|
||||
* (boolean) Whether widget provider info would be saved to / loaded from system persistence
|
||||
* layer as opposed to individual manifests in respective apps.
|
||||
*/
|
||||
public static final String PERSISTS_WIDGET_PROVIDER_INFO = "persists_widget_provider_info";
|
||||
|
||||
private SystemUiDeviceConfigFlags() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,11 @@ import static android.content.Context.KEYGUARD_SERVICE;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.content.res.Resources.ID_NULL;
|
||||
import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
|
||||
|
||||
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerInternal;
|
||||
@@ -83,6 +85,7 @@ import android.os.SystemClock;
|
||||
import android.os.Trace;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.service.appwidget.AppWidgetServiceDumpProto;
|
||||
import android.service.appwidget.WidgetProto;
|
||||
import android.text.TextUtils;
|
||||
@@ -113,6 +116,7 @@ import com.android.internal.app.SuspendedAppActivity;
|
||||
import com.android.internal.app.UnlaunchableAppActivity;
|
||||
import com.android.internal.appwidget.IAppWidgetHost;
|
||||
import com.android.internal.appwidget.IAppWidgetService;
|
||||
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
|
||||
import com.android.internal.os.BackgroundThread;
|
||||
import com.android.internal.os.SomeArgs;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
@@ -150,6 +154,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
private static final String TAG = "AppWidgetServiceImpl";
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
private static final boolean DEBUG_PROVIDER_INFO_CACHE = true;
|
||||
|
||||
private static final String OLD_KEYGUARD_HOST_PACKAGE = "android";
|
||||
private static final String NEW_KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
|
||||
@@ -246,6 +251,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
|
||||
private boolean mSafeMode;
|
||||
private int mMaxWidgetBitmapMemory;
|
||||
private boolean mIsProviderInfoPersisted;
|
||||
|
||||
AppWidgetServiceImpl(Context context) {
|
||||
mContext = context;
|
||||
@@ -263,6 +269,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
mCallbackHandler = new CallbackHandler(mContext.getMainLooper());
|
||||
mBackupRestoreController = new BackupRestoreController();
|
||||
mSecurityPolicy = new SecurityPolicy();
|
||||
mIsProviderInfoPersisted = !ActivityManager.isLowRamDeviceStatic()
|
||||
&& DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
|
||||
SystemUiDeviceConfigFlags.PERSISTS_WIDGET_PROVIDER_INFO, true);
|
||||
if (DEBUG_PROVIDER_INFO_CACHE && !mIsProviderInfoPersisted) {
|
||||
Slog.d(TAG, "App widget provider info will not be persisted on this device");
|
||||
}
|
||||
|
||||
computeMaximumWidgetBitmapMemory();
|
||||
registerBroadcastReceiver();
|
||||
@@ -607,10 +619,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void ensureGroupStateLoadedLocked(int userId) {
|
||||
ensureGroupStateLoadedLocked(userId, /* enforceUserUnlockingOrUnlocked */ true );
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void ensureGroupStateLoadedLocked(int userId, boolean enforceUserUnlockingOrUnlocked) {
|
||||
if (enforceUserUnlockingOrUnlocked && !isUserRunningAndUnlocked(userId)) {
|
||||
throw new IllegalStateException(
|
||||
@@ -2184,6 +2198,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void loadGroupWidgetProvidersLocked(int[] profileIds) {
|
||||
List<ResolveInfo> allReceivers = null;
|
||||
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
|
||||
@@ -2409,7 +2424,24 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
}
|
||||
}
|
||||
|
||||
private static void serializeProvider(TypedXmlSerializer out, Provider p) throws IOException {
|
||||
private static void serializeProvider(
|
||||
@NonNull final TypedXmlSerializer out, @NonNull final Provider p) throws IOException {
|
||||
Objects.requireNonNull(out);
|
||||
Objects.requireNonNull(p);
|
||||
serializeProviderInner(out, p, false /* persistsProviderInfo */);
|
||||
}
|
||||
|
||||
private static void serializeProviderWithProviderInfo(
|
||||
@NonNull final TypedXmlSerializer out, @NonNull final Provider p) throws IOException {
|
||||
Objects.requireNonNull(out);
|
||||
Objects.requireNonNull(p);
|
||||
serializeProviderInner(out, p, true /* persistsProviderInfo */);
|
||||
}
|
||||
|
||||
private static void serializeProviderInner(@NonNull final TypedXmlSerializer out,
|
||||
@NonNull final Provider p, final boolean persistsProviderInfo) throws IOException {
|
||||
Objects.requireNonNull(out);
|
||||
Objects.requireNonNull(p);
|
||||
out.startTag(null, "p");
|
||||
out.attribute(null, "pkg", p.id.componentName.getPackageName());
|
||||
out.attribute(null, "cl", p.id.componentName.getClassName());
|
||||
@@ -2417,6 +2449,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
if (!TextUtils.isEmpty(p.infoTag)) {
|
||||
out.attribute(null, "info_tag", p.infoTag);
|
||||
}
|
||||
if (DEBUG_PROVIDER_INFO_CACHE && persistsProviderInfo && !p.mInfoParsed) {
|
||||
Slog.d(TAG, "Provider info from " + p.id.componentName + " won't be persisted.");
|
||||
}
|
||||
if (persistsProviderInfo && p.mInfoParsed) {
|
||||
AppWidgetXmlUtil.writeAppWidgetProviderInfoLocked(out, p.info);
|
||||
}
|
||||
out.endTag(null, "p");
|
||||
}
|
||||
|
||||
@@ -2768,6 +2806,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
}
|
||||
|
||||
// only call from initialization -- it assumes that the data structures are all empty
|
||||
@GuardedBy("mLock")
|
||||
private void loadGroupStateLocked(int[] profileIds) {
|
||||
// We can bind the widgets to host and providers only after
|
||||
// reading the host and providers for all users since a widget
|
||||
@@ -2959,6 +2998,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
return false;
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void saveStateLocked(int userId) {
|
||||
tagProvidersAndHosts();
|
||||
|
||||
@@ -3012,6 +3052,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private boolean writeProfileStateToFileLocked(FileOutputStream stream, int userId) {
|
||||
int N;
|
||||
|
||||
@@ -3028,7 +3069,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
if (provider.getUserId() != userId) {
|
||||
continue;
|
||||
}
|
||||
if (provider.shouldBePersisted()) {
|
||||
if (mIsProviderInfoPersisted) {
|
||||
serializeProviderWithProviderInfo(out, provider);
|
||||
} else if (provider.shouldBePersisted()) {
|
||||
serializeProvider(out, provider);
|
||||
}
|
||||
}
|
||||
@@ -3074,6 +3117,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private int readProfileStateFromFileLocked(FileInputStream stream, int userId,
|
||||
List<LoadedWidgetState> outLoadedWidgets) {
|
||||
int version = -1;
|
||||
@@ -3127,6 +3171,18 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
provider.zombie = true;
|
||||
provider.id = providerId;
|
||||
mProviders.add(provider);
|
||||
} else if (mIsProviderInfoPersisted) {
|
||||
final AppWidgetProviderInfo info =
|
||||
AppWidgetXmlUtil.readAppWidgetProviderInfoLocked(parser);
|
||||
if (DEBUG_PROVIDER_INFO_CACHE && info == null) {
|
||||
Slog.d(TAG, "Unable to load widget provider info from xml for "
|
||||
+ providerId.componentName);
|
||||
}
|
||||
if (info != null) {
|
||||
info.provider = providerId.componentName;
|
||||
info.providerInfo = providerInfo;
|
||||
provider.setInfoLocked(info);
|
||||
}
|
||||
}
|
||||
|
||||
final int providerTag = parser.getAttributeIntHex(null, "tag",
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.appwidget;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedXmlPullParser;
|
||||
import android.util.TypedXmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class AppWidgetXmlUtil {
|
||||
|
||||
private static final String ATTR_MIN_WIDTH = "min_width";
|
||||
private static final String ATTR_MIN_HEIGHT = "min_height";
|
||||
private static final String ATTR_MIN_RESIZE_WIDTH = "min_resize_width";
|
||||
private static final String ATTR_MIN_RESIZE_HEIGHT = "min_resize_height";
|
||||
private static final String ATTR_MAX_RESIZE_WIDTH = "max_resize_width";
|
||||
private static final String ATTR_MAX_RESIZE_HEIGHT = "max_resize_height";
|
||||
private static final String ATTR_TARGET_CELL_WIDTH = "target_cell_width";
|
||||
private static final String ATTR_TARGET_CELL_HEIGHT = "target_cell_height";
|
||||
private static final String ATTR_UPDATE_PERIOD_MILLIS = "update_period_millis";
|
||||
private static final String ATTR_INITIAL_LAYOUT = "initial_layout";
|
||||
private static final String ATTR_INITIAL_KEYGUARD_LAYOUT = "initial_keyguard_layout";
|
||||
private static final String ATTR_CONFIGURE = "configure";
|
||||
private static final String ATTR_LABEL = "label";
|
||||
private static final String ATTR_ICON = "icon";
|
||||
private static final String ATTR_PREVIEW_IMAGE = "preview_image";
|
||||
private static final String ATTR_PREVIEW_LAYOUT = "preview_layout";
|
||||
private static final String ATTR_AUTO_ADVANCED_VIEW_ID = "auto_advance_view_id";
|
||||
private static final String ATTR_RESIZE_MODE = "resize_mode";
|
||||
private static final String ATTR_WIDGET_CATEGORY = "widget_category";
|
||||
private static final String ATTR_WIDGET_FEATURES = "widget_features";
|
||||
private static final String ATTR_DESCRIPTION_RES = "description_res";
|
||||
private static final String ATTR_OS_FINGERPRINT = "os_fingerprint";
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static void writeAppWidgetProviderInfoLocked(@NonNull final TypedXmlSerializer out,
|
||||
@NonNull final AppWidgetProviderInfo info) throws IOException {
|
||||
Objects.requireNonNull(out);
|
||||
Objects.requireNonNull(info);
|
||||
out.attributeInt(null, ATTR_MIN_WIDTH, info.minWidth);
|
||||
out.attributeInt(null, ATTR_MIN_HEIGHT, info.minHeight);
|
||||
out.attributeInt(null, ATTR_MIN_RESIZE_WIDTH, info.minResizeWidth);
|
||||
out.attributeInt(null, ATTR_MIN_RESIZE_HEIGHT, info.minResizeHeight);
|
||||
out.attributeInt(null, ATTR_MAX_RESIZE_WIDTH, info.maxResizeWidth);
|
||||
out.attributeInt(null, ATTR_MAX_RESIZE_HEIGHT, info.maxResizeHeight);
|
||||
out.attributeInt(null, ATTR_TARGET_CELL_WIDTH, info.targetCellWidth);
|
||||
out.attributeInt(null, ATTR_TARGET_CELL_HEIGHT, info.targetCellHeight);
|
||||
out.attributeInt(null, ATTR_UPDATE_PERIOD_MILLIS, info.updatePeriodMillis);
|
||||
out.attributeInt(null, ATTR_INITIAL_LAYOUT, info.initialLayout);
|
||||
out.attributeInt(null, ATTR_INITIAL_KEYGUARD_LAYOUT, info.initialKeyguardLayout);
|
||||
if (info.configure != null) {
|
||||
out.attribute(null, ATTR_CONFIGURE, info.configure.flattenToShortString());
|
||||
}
|
||||
out.attribute(null, ATTR_LABEL, info.label);
|
||||
out.attributeInt(null, ATTR_ICON, info.icon);
|
||||
out.attributeInt(null, ATTR_PREVIEW_IMAGE, info.previewImage);
|
||||
out.attributeInt(null, ATTR_PREVIEW_LAYOUT, info.previewLayout);
|
||||
out.attributeInt(null, ATTR_AUTO_ADVANCED_VIEW_ID, info.autoAdvanceViewId);
|
||||
out.attributeInt(null, ATTR_RESIZE_MODE, info.resizeMode);
|
||||
out.attributeInt(null, ATTR_WIDGET_CATEGORY, info.widgetCategory);
|
||||
out.attributeInt(null, ATTR_WIDGET_FEATURES, info.widgetFeatures);
|
||||
out.attributeInt(null, ATTR_DESCRIPTION_RES, info.descriptionRes);
|
||||
out.attribute(null, ATTR_OS_FINGERPRINT, Build.FINGERPRINT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public static AppWidgetProviderInfo readAppWidgetProviderInfoLocked(
|
||||
@NonNull final TypedXmlPullParser parser) {
|
||||
Objects.requireNonNull(parser);
|
||||
final String fingerprint = parser.getAttributeValue(null, ATTR_OS_FINGERPRINT);
|
||||
if (!Build.FINGERPRINT.equals(fingerprint)) {
|
||||
return null;
|
||||
}
|
||||
final AppWidgetProviderInfo info = new AppWidgetProviderInfo();
|
||||
info.minWidth = parser.getAttributeInt(null, ATTR_MIN_WIDTH, 0);
|
||||
info.minHeight = parser.getAttributeInt(null, ATTR_MIN_HEIGHT, 0);
|
||||
info.minResizeWidth = parser.getAttributeInt(null, ATTR_MIN_RESIZE_WIDTH, 0);
|
||||
info.minResizeWidth = parser.getAttributeInt(null, ATTR_MIN_RESIZE_HEIGHT, 0);
|
||||
info.maxResizeWidth = parser.getAttributeInt(null, ATTR_MAX_RESIZE_WIDTH, 0);
|
||||
info.maxResizeHeight = parser.getAttributeInt(null, ATTR_MAX_RESIZE_HEIGHT, 0);
|
||||
info.targetCellWidth = parser.getAttributeInt(null, ATTR_TARGET_CELL_WIDTH, 0);
|
||||
info.targetCellHeight = parser.getAttributeInt(null, ATTR_TARGET_CELL_HEIGHT, 0);
|
||||
info.updatePeriodMillis = parser.getAttributeInt(null, ATTR_UPDATE_PERIOD_MILLIS, 0);
|
||||
info.initialLayout = parser.getAttributeInt(null, ATTR_INITIAL_LAYOUT, 0);
|
||||
info.initialKeyguardLayout = parser.getAttributeInt(
|
||||
null, ATTR_INITIAL_KEYGUARD_LAYOUT, 0);
|
||||
final String configure = parser.getAttributeValue(null, ATTR_CONFIGURE);
|
||||
if (!TextUtils.isEmpty(configure)) {
|
||||
info.configure = ComponentName.unflattenFromString(configure);
|
||||
}
|
||||
info.label = parser.getAttributeValue(null, ATTR_LABEL);
|
||||
info.icon = parser.getAttributeInt(null, ATTR_ICON, 0);
|
||||
info.previewImage = parser.getAttributeInt(null, ATTR_PREVIEW_IMAGE, 0);
|
||||
info.previewLayout = parser.getAttributeInt(null, ATTR_PREVIEW_LAYOUT, 0);
|
||||
info.autoAdvanceViewId = parser.getAttributeInt(null, ATTR_AUTO_ADVANCED_VIEW_ID, 0);
|
||||
info.resizeMode = parser.getAttributeInt(null, ATTR_RESIZE_MODE, 0);
|
||||
info.widgetCategory = parser.getAttributeInt(null, ATTR_WIDGET_CATEGORY, 0);
|
||||
info.widgetFeatures = parser.getAttributeInt(null, ATTR_WIDGET_FEATURES, 0);
|
||||
info.descriptionRes = parser.getAttributeInt(null, ATTR_DESCRIPTION_RES, 0);
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,9 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.AppOpsManagerInternal;
|
||||
import android.app.admin.DevicePolicyManagerInternal;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetManagerInternal;
|
||||
@@ -39,11 +42,16 @@ import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.ShortcutServiceInternal;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.AtomicFile;
|
||||
import android.util.TypedXmlPullParser;
|
||||
import android.util.TypedXmlSerializer;
|
||||
import android.util.Xml;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.android.frameworks.servicestests.R;
|
||||
@@ -51,9 +59,16 @@ import com.android.internal.appwidget.IAppWidgetHost;
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
@@ -77,6 +92,8 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase {
|
||||
private AppWidgetManager mManager;
|
||||
|
||||
private ShortcutServiceInternal mMockShortcutService;
|
||||
private PackageManagerInternal mMockPackageManager;
|
||||
private AppOpsManagerInternal mMockAppOpsManagerInternal;
|
||||
private IAppWidgetHost mMockHost;
|
||||
|
||||
@Override
|
||||
@@ -85,6 +102,8 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase {
|
||||
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
|
||||
LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
|
||||
LocalServices.removeServiceForTest(AppWidgetManagerInternal.class);
|
||||
LocalServices.removeServiceForTest(PackageManagerInternal.class);
|
||||
LocalServices.removeServiceForTest(AppOpsManagerInternal.class);
|
||||
|
||||
mTestContext = new TestContext();
|
||||
mPkgName = mTestContext.getOpPackageName();
|
||||
@@ -92,9 +111,16 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase {
|
||||
mManager = new AppWidgetManager(mTestContext, mService);
|
||||
|
||||
mMockShortcutService = mock(ShortcutServiceInternal.class);
|
||||
mMockPackageManager = mock(PackageManagerInternal.class);
|
||||
mMockAppOpsManagerInternal = mock(AppOpsManagerInternal.class);
|
||||
mMockHost = mock(IAppWidgetHost.class);
|
||||
LocalServices.addService(ShortcutServiceInternal.class, mMockShortcutService);
|
||||
LocalServices.addService(PackageManagerInternal.class, mMockPackageManager);
|
||||
LocalServices.addService(AppOpsManagerInternal.class, mMockAppOpsManagerInternal);
|
||||
when(mMockPackageManager.filterAppAccess(anyString(), anyInt(), anyInt()))
|
||||
.thenReturn(false);
|
||||
mService.onStart();
|
||||
mService.systemServicesReady();
|
||||
}
|
||||
|
||||
public void testLoadDescription() {
|
||||
@@ -323,6 +349,34 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase {
|
||||
assertThat(info.previewLayout).isEqualTo(R.layout.widget_preview);
|
||||
}
|
||||
|
||||
public void testWidgetProviderInfoPersistence() throws IOException {
|
||||
final AppWidgetProviderInfo original = new AppWidgetProviderInfo();
|
||||
original.minWidth = 40;
|
||||
original.minHeight = 40;
|
||||
original.maxResizeWidth = 250;
|
||||
original.maxResizeHeight = 120;
|
||||
original.targetCellWidth = 1;
|
||||
original.targetCellHeight = 1;
|
||||
original.updatePeriodMillis = 86400000;
|
||||
original.previewLayout = R.layout.widget_preview;
|
||||
original.label = "test";
|
||||
|
||||
final File file = new File(mTestContext.getDataDir(), "appwidget_provider_info.xml");
|
||||
saveWidgetProviderInfoLocked(file, original);
|
||||
final AppWidgetProviderInfo target = loadAppWidgetProviderInfoLocked(file);
|
||||
|
||||
assertThat(target.minWidth).isEqualTo(original.minWidth);
|
||||
assertThat(target.minHeight).isEqualTo(original.minHeight);
|
||||
assertThat(target.minResizeWidth).isEqualTo(original.minResizeWidth);
|
||||
assertThat(target.minResizeHeight).isEqualTo(original.minResizeHeight);
|
||||
assertThat(target.maxResizeWidth).isEqualTo(original.maxResizeWidth);
|
||||
assertThat(target.maxResizeHeight).isEqualTo(original.maxResizeHeight);
|
||||
assertThat(target.targetCellWidth).isEqualTo(original.targetCellWidth);
|
||||
assertThat(target.targetCellHeight).isEqualTo(original.targetCellHeight);
|
||||
assertThat(target.updatePeriodMillis).isEqualTo(original.updatePeriodMillis);
|
||||
assertThat(target.previewLayout).isEqualTo(original.previewLayout);
|
||||
}
|
||||
|
||||
private int setupHostAndWidget() {
|
||||
List<PendingHostUpdate> updates = mService.startListening(
|
||||
mMockHost, mPkgName, HOST_ID, new int[0]).getList();
|
||||
@@ -353,6 +407,44 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase {
|
||||
return mTestContext.getResources().getInteger(resId);
|
||||
}
|
||||
|
||||
private static void saveWidgetProviderInfoLocked(@NonNull final File dst,
|
||||
@Nullable final AppWidgetProviderInfo info)
|
||||
throws IOException {
|
||||
Objects.requireNonNull(dst);
|
||||
if (info == null) {
|
||||
return;
|
||||
}
|
||||
final AtomicFile file = new AtomicFile(dst);
|
||||
final FileOutputStream stream = file.startWrite();
|
||||
final TypedXmlSerializer out = Xml.resolveSerializer(stream);
|
||||
out.startDocument(null, true);
|
||||
out.startTag(null, "p");
|
||||
AppWidgetXmlUtil.writeAppWidgetProviderInfoLocked(out, info);
|
||||
out.endTag(null, "p");
|
||||
out.endDocument();
|
||||
file.finishWrite(stream);
|
||||
}
|
||||
|
||||
public static AppWidgetProviderInfo loadAppWidgetProviderInfoLocked(@NonNull final File dst) {
|
||||
Objects.requireNonNull(dst);
|
||||
final AtomicFile file = new AtomicFile(dst);
|
||||
try (FileInputStream stream = file.openRead()) {
|
||||
final TypedXmlPullParser parser = Xml.resolvePullParser(stream);
|
||||
int type;
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& type != XmlPullParser.START_TAG) {
|
||||
// drain whitespace, comments, etc.
|
||||
}
|
||||
final String nodeName = parser.getName();
|
||||
if (!"p".equals(nodeName)) {
|
||||
return null;
|
||||
}
|
||||
return AppWidgetXmlUtil.readAppWidgetProviderInfoLocked(parser);
|
||||
} catch (IOException | XmlPullParserException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestContext extends ContextWrapper {
|
||||
|
||||
public TestContext() {
|
||||
|
||||
Reference in New Issue
Block a user