Merge "Combining widget broadcasts" into tm-qpr-dev am: e6e061e5d5
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20055672 Change-Id: Idaecc02a707948eb9ac1f00d3a704bb1810e2907 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -42,12 +42,15 @@ import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.android.internal.appwidget.IAppWidgetService;
|
||||
import com.android.internal.os.BackgroundThread;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Updates AppWidget state; gets information about installed AppWidget providers and other
|
||||
@@ -63,6 +66,7 @@ import java.util.List;
|
||||
@RequiresFeature(PackageManager.FEATURE_APP_WIDGETS)
|
||||
public class AppWidgetManager {
|
||||
|
||||
|
||||
/**
|
||||
* Activity action to launch from your {@link AppWidgetHost} activity when you want to
|
||||
* pick an AppWidget to display. The AppWidget picker activity will be launched.
|
||||
@@ -331,6 +335,17 @@ public class AppWidgetManager {
|
||||
@BroadcastBehavior(explicitOnly = true)
|
||||
public static final String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";
|
||||
|
||||
/**
|
||||
* A combination broadcast of APPWIDGET_ENABLED and APPWIDGET_UPDATE.
|
||||
* Sent during boot time and when the host is binding the widget for the very first time
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
|
||||
@BroadcastBehavior(explicitOnly = true)
|
||||
public static final String ACTION_APPWIDGET_ENABLE_AND_UPDATE = "android.appwidget.action"
|
||||
+ ".APPWIDGET_ENABLE_AND_UPDATE";
|
||||
|
||||
/**
|
||||
* Sent when the custom extras for an AppWidget change.
|
||||
*
|
||||
@@ -456,6 +471,8 @@ public class AppWidgetManager {
|
||||
public static final String ACTION_APPWIDGET_HOST_RESTORED
|
||||
= "android.appwidget.action.APPWIDGET_HOST_RESTORED";
|
||||
|
||||
private static final String TAG = "AppWidgetManager";
|
||||
|
||||
/**
|
||||
* An intent extra that contains multiple appWidgetIds. These are id values as
|
||||
* they were provided to the application during a recent restore from backup. It is
|
||||
@@ -511,6 +528,26 @@ public class AppWidgetManager {
|
||||
mPackageName = context.getOpPackageName();
|
||||
mService = service;
|
||||
mDisplayMetrics = context.getResources().getDisplayMetrics();
|
||||
if (mService == null) {
|
||||
return;
|
||||
}
|
||||
BackgroundThread.getExecutor().execute(() -> {
|
||||
try {
|
||||
mService.notifyProviderInheritance(getInstalledProvidersForPackage(mPackageName,
|
||||
null)
|
||||
.stream().filter(Objects::nonNull)
|
||||
.map(info -> info.provider).filter(p -> {
|
||||
try {
|
||||
Class clazz = Class.forName(p.getClassName());
|
||||
return AppWidgetProvider.class.isAssignableFrom(clazz);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}).toArray(ComponentName[]::new));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Nofity service of inheritance info", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -58,7 +58,12 @@ public class AppWidgetProvider extends BroadcastReceiver {
|
||||
// Protect against rogue update broadcasts (not really a security issue,
|
||||
// just filter bad broacasts out so subclasses are less likely to crash).
|
||||
String action = intent.getAction();
|
||||
if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
|
||||
if (AppWidgetManager.ACTION_APPWIDGET_ENABLE_AND_UPDATE.equals(action)) {
|
||||
this.onReceive(context, new Intent(intent)
|
||||
.setAction(AppWidgetManager.ACTION_APPWIDGET_ENABLED));
|
||||
this.onReceive(context, new Intent(intent)
|
||||
.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE));
|
||||
} else if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras != null) {
|
||||
int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
|
||||
|
||||
@@ -578,6 +578,11 @@ public final class SystemUiDeviceConfigFlags {
|
||||
*/
|
||||
public static final String CLIPBOARD_OVERLAY_SHOW_ACTIONS = "clipboard_overlay_show_actions";
|
||||
|
||||
/**
|
||||
* (boolean) Whether to combine the broadcasts APPWIDGET_ENABLED and APPWIDGET_UPDATE
|
||||
*/
|
||||
public static final String COMBINED_BROADCAST_ENABLED = "combined_broadcast_enabled";
|
||||
|
||||
private SystemUiDeviceConfigFlags() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +142,7 @@
|
||||
<protected-broadcast android:name="android.appwidget.action.APPWIDGET_ENABLED" />
|
||||
<protected-broadcast android:name="android.appwidget.action.APPWIDGET_HOST_RESTORED" />
|
||||
<protected-broadcast android:name="android.appwidget.action.APPWIDGET_RESTORED" />
|
||||
<protected-broadcast android:name="android.appwidget.action.APPWIDGET_ENABLE_AND_UPDATE" />
|
||||
|
||||
<protected-broadcast android:name="android.os.action.SETTING_RESTORED" />
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerInternal;
|
||||
@@ -254,11 +255,13 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
private boolean mSafeMode;
|
||||
private int mMaxWidgetBitmapMemory;
|
||||
private boolean mIsProviderInfoPersisted;
|
||||
private boolean mIsCombinedBroadcastEnabled;
|
||||
|
||||
AppWidgetServiceImpl(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG)
|
||||
public void onStart() {
|
||||
mPackageManager = AppGlobals.getPackageManager();
|
||||
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
|
||||
@@ -277,6 +280,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
mIsProviderInfoPersisted = !ActivityManager.isLowRamDeviceStatic()
|
||||
&& DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
|
||||
SystemUiDeviceConfigFlags.PERSISTS_WIDGET_PROVIDER_INFO, true);
|
||||
mIsCombinedBroadcastEnabled = DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
|
||||
SystemUiDeviceConfigFlags.COMBINED_BROADCAST_ENABLED, true);
|
||||
if (DEBUG_PROVIDER_INFO_CACHE && !mIsProviderInfoPersisted) {
|
||||
Slog.d(TAG, "App widget provider info will not be persisted on this device");
|
||||
}
|
||||
@@ -1123,16 +1128,16 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
|
||||
final int widgetCount = provider.widgets.size();
|
||||
if (widgetCount == 1) {
|
||||
// Tell the provider that it's ready.
|
||||
sendEnableIntentLocked(provider);
|
||||
// If we are binding the very first widget from a provider, we will send
|
||||
// a combined broadcast or 2 separate broadcasts to tell the provider that
|
||||
// it's ready, and we need them to provide the update now.
|
||||
sendEnableAndUpdateIntentLocked(provider, new int[]{appWidgetId});
|
||||
} else {
|
||||
// For any widget other then the first one, we just send update intent
|
||||
// as we normally would.
|
||||
sendUpdateIntentLocked(provider, new int[]{appWidgetId});
|
||||
}
|
||||
|
||||
// Send an update now -- We need this update now, and just for this appWidgetId.
|
||||
// It's less critical when the next one happens, so when we schedule the next one,
|
||||
// we add updatePeriodMillis to its start time. That time will have some slop,
|
||||
// but that's okay.
|
||||
sendUpdateIntentLocked(provider, new int[] {appWidgetId});
|
||||
|
||||
// Schedule the future updates.
|
||||
registerForBroadcastsLocked(provider, getWidgetIds(provider.widgets));
|
||||
|
||||
@@ -2361,6 +2366,22 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
cancelBroadcastsLocked(provider);
|
||||
}
|
||||
|
||||
private void sendEnableAndUpdateIntentLocked(@NonNull Provider p, int[] appWidgetIds) {
|
||||
final boolean canSendCombinedBroadcast = mIsCombinedBroadcastEnabled && p.info != null
|
||||
&& p.info.isExtendedFromAppWidgetProvider;
|
||||
if (!canSendCombinedBroadcast) {
|
||||
// If this function is called by mistake, send two separate broadcasts instead
|
||||
sendEnableIntentLocked(p);
|
||||
sendUpdateIntentLocked(p, appWidgetIds);
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLE_AND_UPDATE);
|
||||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
|
||||
intent.setComponent(p.id.componentName);
|
||||
sendBroadcastAsUser(intent, p.id.getProfile());
|
||||
}
|
||||
|
||||
private void sendEnableIntentLocked(Provider p) {
|
||||
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
|
||||
intent.setComponent(p.id.componentName);
|
||||
@@ -2852,7 +2873,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
if (provider.widgets.size() > 0) {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
|
||||
"appwidget init " + provider.id.componentName.getPackageName());
|
||||
sendEnableIntentLocked(provider);
|
||||
provider.widgets.forEach(widget -> {
|
||||
widget.trackingUpdate = true;
|
||||
Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
|
||||
@@ -2861,7 +2881,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
Log.i(TAG, "Widget update scheduled on unlock " + widget.toString());
|
||||
});
|
||||
int[] appWidgetIds = getWidgetIds(provider.widgets);
|
||||
sendUpdateIntentLocked(provider, appWidgetIds);
|
||||
sendEnableAndUpdateIntentLocked(provider, appWidgetIds);
|
||||
registerForBroadcastsLocked(provider, appWidgetIds);
|
||||
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase {
|
||||
}
|
||||
|
||||
public void testRequestPinAppWidget() {
|
||||
ComponentName provider = new ComponentName(mTestContext, DummyAppWidget.class);
|
||||
ComponentName provider = new ComponentName(mTestContext, TestAppWidgetProvider.class);
|
||||
// Set up users.
|
||||
when(mMockShortcutService.requestPinAppWidget(anyString(),
|
||||
any(AppWidgetProviderInfo.class), eq(null), eq(null), anyInt()))
|
||||
@@ -289,6 +289,16 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase {
|
||||
assertEquals(4, updates.size());
|
||||
}
|
||||
|
||||
public void testReceiveBroadcastBehavior_enableAndUpdate() {
|
||||
TestAppWidgetProvider testAppWidgetProvider = new TestAppWidgetProvider();
|
||||
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLE_AND_UPDATE);
|
||||
|
||||
testAppWidgetProvider.onReceive(mTestContext, intent);
|
||||
|
||||
assertTrue(testAppWidgetProvider.isBehaviorSuccess());
|
||||
}
|
||||
|
||||
|
||||
public void testUpdatesReceived_queueNotEmpty_multipleWidgetIdProvided() {
|
||||
int widgetId = setupHostAndWidget();
|
||||
int widgetId2 = bindNewWidget();
|
||||
@@ -385,7 +395,7 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase {
|
||||
}
|
||||
|
||||
private int bindNewWidget() {
|
||||
ComponentName provider = new ComponentName(mTestContext, DummyAppWidget.class);
|
||||
ComponentName provider = new ComponentName(mTestContext, TestAppWidgetProvider.class);
|
||||
int widgetId = mService.allocateAppWidgetId(mPkgName, HOST_ID);
|
||||
assertTrue(mManager.bindAppWidgetIdIfAllowed(widgetId, provider));
|
||||
assertEquals(provider, mManager.getAppWidgetInfo(widgetId).provider);
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
/**
|
||||
* Placeholder widget for testing
|
||||
*/
|
||||
public class DummyAppWidget extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 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.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Placeholder widget for testing
|
||||
*/
|
||||
public class TestAppWidgetProvider extends AppWidgetProvider {
|
||||
private boolean mEnabled;
|
||||
private boolean mUpdated;
|
||||
|
||||
TestAppWidgetProvider() {
|
||||
super();
|
||||
mEnabled = false;
|
||||
mUpdated = false;
|
||||
}
|
||||
|
||||
public boolean isBehaviorSuccess() {
|
||||
return mEnabled && mUpdated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
|
||||
int[] appWidgetids) {
|
||||
mUpdated = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnabled(Context context) {
|
||||
mEnabled = true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user