1 /* 2 * Copyright (C) 2015 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.cts.widgetprovider; 18 19 import android.app.Service; 20 import android.appwidget.AppWidgetHost; 21 import android.appwidget.AppWidgetHostView; 22 import android.appwidget.AppWidgetManager; 23 import android.appwidget.AppWidgetProviderInfo; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.os.Bundle; 27 import android.os.Handler; 28 import android.os.HandlerThread; 29 import android.os.IBinder; 30 import android.os.Looper; 31 import android.os.Message; 32 import android.os.Messenger; 33 import android.os.RemoteException; 34 import android.os.UserHandle; 35 import android.os.UserManager; 36 import android.util.Log; 37 import android.widget.RemoteViews; 38 39 import java.util.List; 40 import java.util.concurrent.Semaphore; 41 import java.util.concurrent.TimeUnit; 42 43 /** 44 * Service that acts as AppWidgetHost that listens to onProvidersChanged callbacks and updates the 45 * internally stored list of profile widgets. The service reacts to messages sent from the device 46 * side tests and returns whether the expected widget provider is currently present or not. 47 */ 48 public class SimpleAppWidgetHostService extends Service { 49 private static final String TAG = "SimpleAppWidgetHostService"; 50 51 private static final int MSG_RESULT = 0; 52 private static final int MSG_PROVIDER_PRESENT = 1; 53 private static final int MSG_PROVIDER_UPDATES = 2; 54 55 private static final int RESULT_UNKNOWN = 0; 56 private static final int RESULT_PRESENT = 1; 57 private static final int RESULT_NOT_PRESENT = 2; 58 private static final int RESULT_INTERRUPTED = 3; 59 private static final int RESULT_TIMEOUT = 4; 60 61 public static final String USER_EXTRA = "user-extra"; 62 public static final String PACKAGE_EXTRA = "package-extra"; 63 public static final String REPLY_EXTRA = "reply-extra"; 64 65 private AppWidgetManager mAppWidgetManager; 66 private SimpleAppWidgetHost mAppWidgetHost; 67 private SimpleAppWidgetHostView mAppWidgetHostView; 68 private int mAppWidgetId; 69 private Messenger mMessenger; 70 private UserHandle mUserHandle; 71 private Semaphore mWidgetUpdateSemaphore = new Semaphore(0); 72 private RemoteViews mRemoteViews; 73 74 class CheckHandler extends Handler { CheckHandler(Looper looper)75 public CheckHandler(Looper looper) { 76 super(looper); 77 } 78 79 @Override handleMessage(Message msg)80 public void handleMessage(Message msg) { 81 Bundle params = null; 82 if (msg.obj instanceof Bundle) { 83 params = (Bundle) (msg.obj); 84 } 85 try { 86 switch (msg.what) { 87 case MSG_PROVIDER_PRESENT: { 88 Log.d(TAG, "MSG_PROVIDER_PRESENT"); 89 int result = RESULT_UNKNOWN; 90 try { 91 AppWidgetProviderInfo info = mAppWidgetHost.getProvider(params); 92 result = (info != null) ? RESULT_PRESENT : RESULT_NOT_PRESENT; 93 if (info != null) { 94 bindAppWidget(info); 95 } 96 } catch (InterruptedException e) { 97 result = RESULT_INTERRUPTED; 98 } 99 msg.replyTo.send(Message.obtain(null, MSG_RESULT, result 100 , 0 /* not used */)); 101 break; 102 } 103 case MSG_PROVIDER_UPDATES: { 104 Log.d(TAG, "MSG_PROVIDER_UPDATES"); 105 int result = RESULT_UNKNOWN; 106 try { 107 updateWidgetViaWidgetId(); 108 boolean update = waitForUpdate(); 109 result = update ? RESULT_PRESENT : RESULT_NOT_PRESENT; 110 } catch (InterruptedException e) { 111 result = RESULT_INTERRUPTED; 112 } 113 msg.replyTo.send(Message.obtain(null, MSG_RESULT, result 114 , 0 /* not used */)); 115 break; 116 } 117 default: 118 super.handleMessage(msg); 119 } 120 } catch (RemoteException e) { 121 Log.e(TAG, "Failed to report test status"); 122 } 123 } 124 } 125 126 @Override onStartCommand(Intent intent, int flags, int startId)127 public int onStartCommand(Intent intent, int flags, int startId) { 128 if (intent == null) { 129 return START_NOT_STICKY; 130 } 131 if ("com.android.cts.widgetprovider.REGISTER_CALLBACK".equals(intent.getAction())) { 132 mUserHandle = getUserHandleArgument(this, USER_EXTRA, intent); 133 Log.d(TAG, "mUserHandle=" + mUserHandle); 134 setup(); 135 } 136 return START_STICKY; 137 } 138 setup()139 private void setup() { 140 HandlerThread handlerThread = new HandlerThread("Widget test callback handler"); 141 handlerThread.start(); 142 mMessenger = new Messenger(new CheckHandler(handlerThread.getLooper())); 143 mAppWidgetManager = (AppWidgetManager) getSystemService(Context.APPWIDGET_SERVICE); 144 mAppWidgetHost = new SimpleAppWidgetHost(this, 0); 145 mAppWidgetHost.deleteHost(); 146 mAppWidgetHost.startListening(); 147 } 148 149 @Override onBind(Intent intent)150 public IBinder onBind(Intent intent) { 151 return mMessenger.getBinder(); 152 } 153 154 @Override onDestroy()155 public void onDestroy() { 156 mAppWidgetHost.stopListening(); 157 mAppWidgetHost.deleteAppWidgetId(mAppWidgetId); 158 mAppWidgetHost.deleteHost(); 159 } 160 bindAppWidget(AppWidgetProviderInfo info)161 private void bindAppWidget(AppWidgetProviderInfo info) { 162 mAppWidgetId = mAppWidgetHost.allocateAppWidgetId(); 163 Log.d(TAG, "Registering app widget with id:" + mAppWidgetId); 164 mAppWidgetManager.bindAppWidgetIdIfAllowed(mAppWidgetId, mUserHandle, info.provider, null); 165 mAppWidgetHostView = (SimpleAppWidgetHostView) mAppWidgetHost.createView(this, 166 mAppWidgetId, info); 167 mRemoteViews = new RemoteViews(info.provider.getPackageName(), R.layout.simple_widget); 168 } 169 getUserHandleArgument(Context context, String key, Intent intent)170 private UserHandle getUserHandleArgument(Context context, String key, 171 Intent intent) { 172 UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 173 int serial = intent.getIntExtra(key, 0); 174 Log.d(TAG, "userId=" + serial); 175 return um.getUserForSerialNumber(serial); 176 } 177 updateWidgetViaWidgetId()178 private void updateWidgetViaWidgetId() { 179 Log.d(TAG, "Forcing widget update via widget id"); 180 mWidgetUpdateSemaphore.drainPermits(); 181 // trigger a widget update 182 mAppWidgetManager.updateAppWidget(mAppWidgetId, mRemoteViews); 183 } 184 waitForUpdate()185 private boolean waitForUpdate() throws InterruptedException { 186 // wait for updateAppWidget to arrive 187 return mWidgetUpdateSemaphore.tryAcquire(20, TimeUnit.SECONDS); 188 } 189 190 private class SimpleAppWidgetHost extends AppWidgetHost { 191 private List<AppWidgetProviderInfo> mProviders; 192 private Semaphore mSemaphore = new Semaphore(0); SimpleAppWidgetHost(Context context, int hostId)193 public SimpleAppWidgetHost(Context context, int hostId) { 194 super(context, hostId); 195 synchronized (this) { 196 mProviders = mAppWidgetManager.getInstalledProvidersForProfile(mUserHandle); 197 } 198 } 199 200 @Override onProvidersChanged()201 protected void onProvidersChanged() { 202 super.onProvidersChanged(); 203 Log.d(TAG, "onProvidersChanged callback received"); 204 synchronized (this) { 205 mProviders = mAppWidgetManager.getInstalledProvidersForProfile(mUserHandle); 206 } 207 mSemaphore.release(); 208 } 209 210 @Override onCreateView(Context context, int id, AppWidgetProviderInfo info)211 protected AppWidgetHostView onCreateView(Context context, int id, AppWidgetProviderInfo info) { 212 return new SimpleAppWidgetHostView(context); 213 } 214 getProvider(Bundle params)215 public AppWidgetProviderInfo getProvider(Bundle params) throws InterruptedException { 216 String packageName = params.getString(PACKAGE_EXTRA); 217 while (mSemaphore.tryAcquire(30, TimeUnit.SECONDS)) { 218 mSemaphore.drainPermits(); 219 Log.d(TAG, "checking for " + packageName + " " + mUserHandle); 220 synchronized (this) { 221 for (AppWidgetProviderInfo providerInfo : mProviders) { 222 if (providerInfo.provider.getPackageName().equals(packageName)) { 223 Log.d(TAG, "Provider exists " + packageName 224 + " for user " + mUserHandle); 225 return providerInfo; 226 } 227 } 228 } 229 } 230 return null; 231 } 232 } 233 234 private class SimpleAppWidgetHostView extends AppWidgetHostView { SimpleAppWidgetHostView(Context context)235 public SimpleAppWidgetHostView(Context context) { 236 super(context); 237 } 238 239 @Override updateAppWidget(RemoteViews views)240 public void updateAppWidget(RemoteViews views) { 241 super.updateAppWidget(views); 242 Log.d(TAG, "Host view received widget update"); 243 mWidgetUpdateSemaphore.release(); 244 } 245 } 246 } 247