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