blob: fd502d83a3300747968d0688e50401ca3c08a82b [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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
17package com.android.server;
18
Winson Chung81f39eb2011-01-11 18:05:01 -080019import java.io.File;
20import java.io.FileDescriptor;
21import java.io.FileInputStream;
22import java.io.FileOutputStream;
23import java.io.IOException;
24import java.io.PrintWriter;
25import java.util.ArrayList;
26import java.util.HashMap;
27import java.util.HashSet;
28import java.util.Iterator;
29import java.util.List;
30import java.util.Locale;
31
Dianne Hackborn2f0b1752011-05-31 17:59:49 -070032import org.apache.commons.logging.impl.SimpleLog;
Winson Chung81f39eb2011-01-11 18:05:01 -080033import org.xmlpull.v1.XmlPullParser;
34import org.xmlpull.v1.XmlPullParserException;
35import org.xmlpull.v1.XmlSerializer;
36
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.app.AlarmManager;
38import android.app.PendingIntent;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070039import android.appwidget.AppWidgetManager;
40import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.content.BroadcastReceiver;
42import android.content.ComponentName;
43import android.content.Context;
44import android.content.Intent;
Winson Chung81f39eb2011-01-11 18:05:01 -080045import android.content.Intent.FilterComparison;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.content.IntentFilter;
Winson Chung81f39eb2011-01-11 18:05:01 -080047import android.content.ServiceConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.content.pm.ActivityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -040049import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.content.pm.PackageInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080051import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.content.pm.ResolveInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080053import android.content.pm.ServiceInfo;
Dianne Hackborn20cb56e2010-03-04 00:58:29 -080054import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.content.res.TypedArray;
56import android.content.res.XmlResourceParser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.net.Uri;
58import android.os.Binder;
59import android.os.Bundle;
Winson Chung81f39eb2011-01-11 18:05:01 -080060import android.os.IBinder;
Marco Nelissen54796e72009-04-30 15:16:30 -070061import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.os.RemoteException;
63import android.os.SystemClock;
64import android.util.AttributeSet;
Winson Chung16c8d8a2011-01-20 16:19:33 -080065import android.util.Log;
Winson Chung81f39eb2011-01-11 18:05:01 -080066import android.util.Pair;
Joe Onorato8a9b2202010-02-26 18:56:32 -080067import android.util.Slog;
Mitsuru Oshima8f25c422009-07-01 00:10:43 -070068import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069import android.util.Xml;
70import android.widget.RemoteViews;
Winson Chung84bbb022011-02-21 13:57:45 -080071import android.widget.RemoteViewsService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070073import com.android.internal.appwidget.IAppWidgetHost;
Winson Chung81f39eb2011-01-11 18:05:01 -080074import com.android.internal.appwidget.IAppWidgetService;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080075import com.android.internal.util.FastXmlSerializer;
Winson Chung81f39eb2011-01-11 18:05:01 -080076import com.android.internal.widget.IRemoteViewsAdapterConnection;
Winson Chung84bbb022011-02-21 13:57:45 -080077import com.android.internal.widget.IRemoteViewsFactory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070079class AppWidgetService extends IAppWidgetService.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080{
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070081 private static final String TAG = "AppWidgetService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070083 private static final String SETTINGS_FILENAME = "appwidgets.xml";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
Joe Onoratobe96b3a2009-07-14 19:49:27 -070085 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086
87 /*
88 * When identifying a Host or Provider based on the calling process, use the uid field.
89 * When identifying a Host or Provider based on a package manager broadcast, use the
90 * package given.
91 */
92
93 static class Provider {
94 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070095 AppWidgetProviderInfo info;
Romain Guya5475592009-07-01 17:20:08 -070096 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 PendingIntent broadcast;
98 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
99
100 int tag; // for use while saving state (the index)
101 }
102
103 static class Host {
104 int uid;
105 int hostId;
106 String packageName;
Romain Guya5475592009-07-01 17:20:08 -0700107 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700108 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
110
111 int tag; // for use while saving state (the index)
112 }
113
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700114 static class AppWidgetId {
115 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 Provider provider;
117 RemoteViews views;
118 Host host;
119 }
120
Winson Chung81f39eb2011-01-11 18:05:01 -0800121 /**
122 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection.
123 * This needs to be a static inner class since a reference to the ServiceConnection is held
124 * globally and may lead us to leak AppWidgetService instances (if there were more than one).
125 */
126 static class ServiceConnectionProxy implements ServiceConnection {
Winson Chung81f39eb2011-01-11 18:05:01 -0800127 private final Pair<Integer, Intent.FilterComparison> mKey;
128 private final IBinder mConnectionCb;
129
Winson Chung16c8d8a2011-01-20 16:19:33 -0800130 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800131 mKey = key;
132 mConnectionCb = connectionCb;
133 }
134 public void onServiceConnected(ComponentName name, IBinder service) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800135 final IRemoteViewsAdapterConnection cb =
Winson Chung81f39eb2011-01-11 18:05:01 -0800136 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
137 try {
138 cb.onServiceConnected(service);
Adam Cohenc2be22c2011-03-16 16:33:53 -0700139 } catch (Exception e) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800140 e.printStackTrace();
141 }
142 }
143 public void onServiceDisconnected(ComponentName name) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800144 disconnect();
145 }
146 public void disconnect() {
147 final IRemoteViewsAdapterConnection cb =
Winson Chung81f39eb2011-01-11 18:05:01 -0800148 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
149 try {
150 cb.onServiceDisconnected();
Adam Cohenc2be22c2011-03-16 16:33:53 -0700151 } catch (Exception e) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800152 e.printStackTrace();
153 }
154 }
155 }
156
Winson Chung84bbb022011-02-21 13:57:45 -0800157 // Manages active connections to RemoteViewsServices
Winson Chung81f39eb2011-01-11 18:05:01 -0800158 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
159 mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
Winson Chung84bbb022011-02-21 13:57:45 -0800160 // Manages persistent references to RemoteViewsServices from different App Widgets
161 private final HashMap<FilterComparison, HashSet<Integer>>
162 mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
Winson Chung81f39eb2011-01-11 18:05:01 -0800163
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 Context mContext;
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700165 Locale mLocale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 PackageManager mPackageManager;
167 AlarmManager mAlarmManager;
Romain Guya5475592009-07-01 17:20:08 -0700168 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700169 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
Romain Guya5475592009-07-01 17:20:08 -0700170 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
171 ArrayList<Host> mHosts = new ArrayList<Host>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 boolean mSafeMode;
173
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700174 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 mContext = context;
176 mPackageManager = context.getPackageManager();
177 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
178 }
179
180 public void systemReady(boolean safeMode) {
181 mSafeMode = safeMode;
182
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700183 loadAppWidgetList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 loadStateLocked();
185
186 // Register for the boot completed broadcast, so we can send the
187 // ENABLE broacasts. If we try to send them now, they time out,
188 // because the system isn't ready to handle them yet.
189 mContext.registerReceiver(mBroadcastReceiver,
190 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
191
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700192 // Register for configuration changes so we can update the names
193 // of the widgets when the locale changes.
194 mContext.registerReceiver(mBroadcastReceiver,
195 new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
196
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 // Register for broadcasts about package install, etc., so we can
198 // update the provider list.
199 IntentFilter filter = new IntentFilter();
200 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
Joe Onoratod070e892011-01-07 20:50:37 -0800201 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
203 filter.addDataScheme("package");
204 mContext.registerReceiver(mBroadcastReceiver, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800205 // Register for events related to sdcard installation.
206 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800207 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
208 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800209 mContext.registerReceiver(mBroadcastReceiver, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 }
211
212 @Override
213 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
214 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
215 != PackageManager.PERMISSION_GRANTED) {
216 pw.println("Permission Denial: can't dump from from pid="
217 + Binder.getCallingPid()
218 + ", uid=" + Binder.getCallingUid());
219 return;
220 }
221
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700222 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 int N = mInstalledProviders.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700224 pw.println("Providers:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 for (int i=0; i<N; i++) {
226 Provider p = mInstalledProviders.get(i);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700227 AppWidgetProviderInfo info = p.info;
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700228 pw.print(" ["); pw.print(i); pw.print("] provider ");
229 pw.print(info.provider.flattenToShortString());
230 pw.println(':');
231 pw.print(" min=("); pw.print(info.minWidth);
232 pw.print("x"); pw.print(info.minHeight);
233 pw.print(") updatePeriodMillis=");
234 pw.print(info.updatePeriodMillis);
235 pw.print(" initialLayout=#");
236 pw.print(Integer.toHexString(info.initialLayout));
237 pw.print(" zombie="); pw.println(p.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 }
239
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700240 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700241 pw.println(" ");
242 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700244 AppWidgetId id = mAppWidgetIds.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700245 pw.print(" ["); pw.print(i); pw.print("] id=");
Romain Guya5475592009-07-01 17:20:08 -0700246 pw.println(id.appWidgetId);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700247 pw.print(" hostId=");
248 pw.print(id.host.hostId); pw.print(' ');
249 pw.print(id.host.packageName); pw.print('/');
250 pw.println(id.host.uid);
251 if (id.provider != null) {
252 pw.print(" provider=");
253 pw.println(id.provider.info.provider.flattenToShortString());
254 }
255 if (id.host != null) {
256 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
257 }
258 if (id.views != null) {
259 pw.print(" views="); pw.println(id.views);
260 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261 }
262
263 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700264 pw.println(" ");
265 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 for (int i=0; i<N; i++) {
267 Host host = mHosts.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700268 pw.print(" ["); pw.print(i); pw.print("] hostId=");
269 pw.print(host.hostId); pw.print(' ');
270 pw.print(host.packageName); pw.print('/');
271 pw.print(host.uid); pw.println(':');
272 pw.print(" callbacks="); pw.println(host.callbacks);
273 pw.print(" instances.size="); pw.print(host.instances.size());
274 pw.print(" zombie="); pw.println(host.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 }
276 }
277 }
278
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700279 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700281 synchronized (mAppWidgetIds) {
282 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283
284 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
285
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700286 AppWidgetId id = new AppWidgetId();
287 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 id.host = host;
289
290 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700291 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292
293 saveStateLocked();
294
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700295 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 }
297 }
298
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700299 public void deleteAppWidgetId(int appWidgetId) {
300 synchronized (mAppWidgetIds) {
301 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700303 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 saveStateLocked();
305 }
306 }
307 }
308
309 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700310 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 int callingUid = getCallingUid();
312 Host host = lookupHostLocked(callingUid, hostId);
313 if (host != null) {
314 deleteHostLocked(host);
315 saveStateLocked();
316 }
317 }
318 }
319
320 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700321 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 int callingUid = getCallingUid();
323 final int N = mHosts.size();
324 boolean changed = false;
325 for (int i=N-1; i>=0; i--) {
326 Host host = mHosts.get(i);
327 if (host.uid == callingUid) {
328 deleteHostLocked(host);
329 changed = true;
330 }
331 }
332 if (changed) {
333 saveStateLocked();
334 }
335 }
336 }
337
338 void deleteHostLocked(Host host) {
339 final int N = host.instances.size();
340 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700341 AppWidgetId id = host.instances.get(i);
342 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 }
344 host.instances.clear();
345 mHosts.remove(host);
346 // it's gone or going away, abruptly drop the callback connection
347 host.callbacks = null;
348 }
349
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700350 void deleteAppWidgetLocked(AppWidgetId id) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800351 // We first unbind all services that are bound to this id
352 unbindAppWidgetRemoteViewsServicesLocked(id);
353
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 Host host = id.host;
355 host.instances.remove(id);
356 pruneHostLocked(host);
357
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700358 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359
360 Provider p = id.provider;
361 if (p != null) {
362 p.instances.remove(id);
363 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700364 // send the broacast saying that this appWidgetId has been deleted
365 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700367 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 mContext.sendBroadcast(intent);
369 if (p.instances.size() == 0) {
370 // cancel the future updates
371 cancelBroadcasts(p);
372
373 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700374 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 intent.setComponent(p.info.provider);
376 mContext.sendBroadcast(intent);
377 }
378 }
379 }
380 }
381
382 void cancelBroadcasts(Provider p) {
383 if (p.broadcast != null) {
384 mAlarmManager.cancel(p.broadcast);
385 long token = Binder.clearCallingIdentity();
386 try {
387 p.broadcast.cancel();
388 } finally {
389 Binder.restoreCallingIdentity(token);
390 }
391 p.broadcast = null;
392 }
393 }
394
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700395 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
396 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
397 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
398 synchronized (mAppWidgetIds) {
399 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 if (id == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700401 throw new IllegalArgumentException("bad appWidgetId");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 }
403 if (id.provider != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700404 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 + id.provider.info.provider);
406 }
407 Provider p = lookupProviderLocked(provider);
408 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700409 throw new IllegalArgumentException("not a appwidget provider: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 }
411 if (p.zombie) {
412 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
413 + " safe mode: " + provider);
414 }
415
416 id.provider = p;
417 p.instances.add(id);
418 int instancesSize = p.instances.size();
419 if (instancesSize == 1) {
420 // tell the provider that it's ready
421 sendEnableIntentLocked(p);
422 }
423
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700424 // send an update now -- We need this update now, and just for this appWidgetId.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 // It's less critical when the next one happens, so when we schdule the next one,
426 // we add updatePeriodMillis to its start time. That time will have some slop,
427 // but that's okay.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700428 sendUpdateIntentLocked(p, new int[] { appWidgetId });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429
430 // schedule the future updates
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700431 registerForBroadcastsLocked(p, getAppWidgetIds(p));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 saveStateLocked();
433 }
434 }
435
Winson Chung84bbb022011-02-21 13:57:45 -0800436 // Binds to a specific RemoteViewsService
Winson Chung81f39eb2011-01-11 18:05:01 -0800437 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
438 synchronized (mAppWidgetIds) {
439 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
440 if (id == null) {
441 throw new IllegalArgumentException("bad appWidgetId");
442 }
443 final ComponentName componentName = intent.getComponent();
444 try {
445 final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
446 PackageManager.GET_PERMISSIONS);
447 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
448 throw new SecurityException("Selected service does not require "
449 + android.Manifest.permission.BIND_REMOTEVIEWS
450 + ": " + componentName);
451 }
452 } catch (PackageManager.NameNotFoundException e) {
453 throw new IllegalArgumentException("Unknown component " + componentName);
454 }
455
Winson Chung16c8d8a2011-01-20 16:19:33 -0800456 // If there is already a connection made for this service intent, then disconnect from
457 // that first. (This does not allow multiple connections to the same service under
458 // the same key)
459 ServiceConnectionProxy conn = null;
Winson Chung84bbb022011-02-21 13:57:45 -0800460 FilterComparison fc = new FilterComparison(intent);
461 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800462 if (mBoundRemoteViewsServices.containsKey(key)) {
463 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
464 conn.disconnect();
465 mContext.unbindService(conn);
466 mBoundRemoteViewsServices.remove(key);
467 }
468
469 // Bind to the RemoteViewsService (which will trigger a callback to the
470 // RemoteViewsAdapter.onServiceConnected())
Winson Chung81f39eb2011-01-11 18:05:01 -0800471 final long token = Binder.clearCallingIdentity();
472 try {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800473 conn = new ServiceConnectionProxy(key, connection);
Winson Chung81f39eb2011-01-11 18:05:01 -0800474 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
475 mBoundRemoteViewsServices.put(key, conn);
476 } finally {
477 Binder.restoreCallingIdentity(token);
478 }
Winson Chung84bbb022011-02-21 13:57:45 -0800479
480 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
481 // when we can call back to the RemoteViewsService later to destroy associated
482 // factories.
Winson Chung22bc69d2011-02-25 15:13:38 -0800483 incrementAppWidgetServiceRefCount(appWidgetId, fc);
Winson Chung81f39eb2011-01-11 18:05:01 -0800484 }
485 }
486
Winson Chung84bbb022011-02-21 13:57:45 -0800487 // Unbinds from a specific RemoteViewsService
Winson Chung81f39eb2011-01-11 18:05:01 -0800488 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
489 synchronized (mAppWidgetIds) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800490 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
491 // RemoteViewsAdapter)
492 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
493 new FilterComparison(intent));
494 if (mBoundRemoteViewsServices.containsKey(key)) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800495 // We don't need to use the appWidgetId until after we are sure there is something
496 // to unbind. Note that this may mask certain issues with apps calling unbind()
497 // more than necessary.
498 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
499 if (id == null) {
500 throw new IllegalArgumentException("bad appWidgetId");
501 }
502
503 ServiceConnectionProxy conn =
504 (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
505 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800506 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800507 mBoundRemoteViewsServices.remove(key);
508 } else {
509 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
Winson Chung81f39eb2011-01-11 18:05:01 -0800510 }
511 }
512 }
513
Winson Chung84bbb022011-02-21 13:57:45 -0800514 // Unbinds from a RemoteViewsService when we delete an app widget
Winson Chung81f39eb2011-01-11 18:05:01 -0800515 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800516 int appWidgetId = id.appWidgetId;
517 // Unbind all connections to Services bound to this AppWidgetId
Winson Chung81f39eb2011-01-11 18:05:01 -0800518 Iterator<Pair<Integer, Intent.FilterComparison>> it =
519 mBoundRemoteViewsServices.keySet().iterator();
Winson Chung81f39eb2011-01-11 18:05:01 -0800520 while (it.hasNext()) {
521 final Pair<Integer, Intent.FilterComparison> key = it.next();
522 if (key.first.intValue() == appWidgetId) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800523 final ServiceConnectionProxy conn = (ServiceConnectionProxy)
524 mBoundRemoteViewsServices.get(key);
525 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800526 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800527 it.remove();
Winson Chung81f39eb2011-01-11 18:05:01 -0800528 }
529 }
Winson Chung84bbb022011-02-21 13:57:45 -0800530
531 // Check if we need to destroy any services (if no other app widgets are
532 // referencing the same service)
533 decrementAppWidgetServiceRefCount(appWidgetId);
534 }
535
536 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
537 private void destroyRemoteViewsService(final Intent intent) {
538 final ServiceConnection conn = new ServiceConnection() {
539 @Override
540 public void onServiceConnected(ComponentName name, IBinder service) {
541 final IRemoteViewsFactory cb =
542 IRemoteViewsFactory.Stub.asInterface(service);
543 try {
544 cb.onDestroy(intent);
Adam Cohen2625fea2011-03-23 17:24:30 -0700545 } catch (RemoteException e) {
546 e.printStackTrace();
547 } catch (RuntimeException e) {
Winson Chung84bbb022011-02-21 13:57:45 -0800548 e.printStackTrace();
549 }
550 mContext.unbindService(this);
551 }
552 @Override
553 public void onServiceDisconnected(android.content.ComponentName name) {
554 // Do nothing
555 }
556 };
557
558 // Bind to the service and remove the static intent->factory mapping in the
559 // RemoteViewsService.
560 final long token = Binder.clearCallingIdentity();
561 try {
562 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
563 } finally {
564 Binder.restoreCallingIdentity(token);
565 }
566 }
567
568 // Adds to the ref-count for a given RemoteViewsService intent
569 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
570 HashSet<Integer> appWidgetIds = null;
571 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
572 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
573 } else {
574 appWidgetIds = new HashSet<Integer>();
575 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
576 }
577 appWidgetIds.add(appWidgetId);
578 }
579
580 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
581 // the ref-count reaches zero.
582 private void decrementAppWidgetServiceRefCount(int appWidgetId) {
583 Iterator<FilterComparison> it =
584 mRemoteViewsServicesAppWidgets.keySet().iterator();
585 while (it.hasNext()) {
586 final FilterComparison key = it.next();
587 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
588 if (ids.remove(appWidgetId)) {
589 // If we have removed the last app widget referencing this service, then we
590 // should destroy it and remove it from this set
591 if (ids.isEmpty()) {
592 destroyRemoteViewsService(key.getIntent());
593 it.remove();
594 }
595 }
596 }
Winson Chung81f39eb2011-01-11 18:05:01 -0800597 }
598
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700599 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
600 synchronized (mAppWidgetIds) {
601 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 if (id != null && id.provider != null && !id.provider.zombie) {
603 return id.provider.info;
604 }
605 return null;
606 }
607 }
608
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700609 public RemoteViews getAppWidgetViews(int appWidgetId) {
610 synchronized (mAppWidgetIds) {
611 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 if (id != null) {
613 return id.views;
614 }
615 return null;
616 }
617 }
618
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700619 public List<AppWidgetProviderInfo> getInstalledProviders() {
620 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621 final int N = mInstalledProviders.size();
Romain Guya5475592009-07-01 17:20:08 -0700622 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623 for (int i=0; i<N; i++) {
624 Provider p = mInstalledProviders.get(i);
625 if (!p.zombie) {
626 result.add(p.info);
627 }
628 }
629 return result;
630 }
631 }
632
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700633 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
634 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800635 return;
636 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700637 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638 return;
639 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700640 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700642 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800643 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700644 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
645 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800646 }
647 }
648 }
649
Adam Cohen2dd21972010-08-15 18:20:04 -0700650 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
651 if (appWidgetIds == null) {
652 return;
653 }
654 if (appWidgetIds.length == 0) {
655 return;
656 }
657 final int N = appWidgetIds.length;
658
659 synchronized (mAppWidgetIds) {
660 for (int i=0; i<N; i++) {
661 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
662 updateAppWidgetInstanceLocked(id, views, true);
663 }
664 }
665 }
666
Winson Chung6394c0e2010-08-16 10:14:56 -0700667 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700668 if (appWidgetIds == null) {
669 return;
670 }
671 if (appWidgetIds.length == 0) {
672 return;
673 }
674 final int N = appWidgetIds.length;
675
676 synchronized (mAppWidgetIds) {
677 for (int i=0; i<N; i++) {
678 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Winson Chung6394c0e2010-08-16 10:14:56 -0700679 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700680 }
681 }
682 }
683
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700684 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
685 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 Provider p = lookupProviderLocked(provider);
687 if (p == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800688 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800689 return;
690 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700691 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 final int N = instances.size();
693 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700694 AppWidgetId id = instances.get(i);
695 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 }
697 }
698 }
699
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700700 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700701 updateAppWidgetInstanceLocked(id, views, false);
702 }
703
704 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700705 // allow for stale appWidgetIds and other badness
706 // lookup also checks that the calling process can access the appWidgetId
707 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700709
710 // We do not want to save this RemoteViews
711 if (!isPartialUpdate) id.views = views;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712
713 // is anyone listening?
714 if (id.host.callbacks != null) {
715 try {
716 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700717 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 } catch (RemoteException e) {
719 // It failed; remove the callback. No need to prune because
720 // we know that this host is still referenced by this instance.
721 id.host.callbacks = null;
722 }
723 }
724 }
725 }
726
Winson Chung6394c0e2010-08-16 10:14:56 -0700727 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700728 // allow for stale appWidgetIds and other badness
729 // lookup also checks that the calling process can access the appWidgetId
730 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
731 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700732 // is anyone listening?
733 if (id.host.callbacks != null) {
734 try {
735 // the lock is held, but this is a oneway call
Winson Chung6394c0e2010-08-16 10:14:56 -0700736 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700737 } catch (RemoteException e) {
738 // It failed; remove the callback. No need to prune because
739 // we know that this host is still referenced by this instance.
740 id.host.callbacks = null;
741 }
742 }
743 }
744 }
745
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700746 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747 List<RemoteViews> updatedViews) {
748 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700749 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
751 host.callbacks = callbacks;
752
753 updatedViews.clear();
754
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700755 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 int N = instances.size();
757 int[] updatedIds = new int[N];
758 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700759 AppWidgetId id = instances.get(i);
760 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761 updatedViews.add(id.views);
762 }
763 return updatedIds;
764 }
765 }
766
767 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700768 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 Host host = lookupHostLocked(getCallingUid(), hostId);
Ken Shirriffe21167a2009-09-23 16:42:53 -0700770 if (host != null) {
771 host.callbacks = null;
772 pruneHostLocked(host);
773 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800774 }
775 }
776
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700777 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700779 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800780 return true;
781 }
782 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700783 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 return true;
785 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700786 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700788 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 return true;
790 }
791 // Nobody else can access it.
792 return false;
793 }
794
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700795 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700797 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700799 AppWidgetId id = mAppWidgetIds.get(i);
800 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800801 return id;
802 }
803 }
804 return null;
805 }
806
807 Provider lookupProviderLocked(ComponentName provider) {
Romain Guyd2671e12010-03-11 18:06:42 -0800808 final String className = provider.getClassName();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 final int N = mInstalledProviders.size();
810 for (int i=0; i<N; i++) {
811 Provider p = mInstalledProviders.get(i);
Romain Guyd2671e12010-03-11 18:06:42 -0800812 if (p.info.provider.equals(provider) || className.equals(p.info.oldName)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 return p;
814 }
815 }
816 return null;
817 }
818
819 Host lookupHostLocked(int uid, int hostId) {
820 final int N = mHosts.size();
821 for (int i=0; i<N; i++) {
822 Host h = mHosts.get(i);
823 if (h.uid == uid && h.hostId == hostId) {
824 return h;
825 }
826 }
827 return null;
828 }
829
830 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
831 final int N = mHosts.size();
832 for (int i=0; i<N; i++) {
833 Host h = mHosts.get(i);
834 if (h.hostId == hostId && h.packageName.equals(packageName)) {
835 return h;
836 }
837 }
838 Host host = new Host();
839 host.packageName = packageName;
840 host.uid = uid;
841 host.hostId = hostId;
842 mHosts.add(host);
843 return host;
844 }
845
846 void pruneHostLocked(Host host) {
847 if (host.instances.size() == 0 && host.callbacks == null) {
848 mHosts.remove(host);
849 }
850 }
851
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700852 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853 PackageManager pm = mPackageManager;
854
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700855 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
857 PackageManager.GET_META_DATA);
858
Bjorn Bringert5f857802010-02-10 23:09:48 +0000859 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800860 for (int i=0; i<N; i++) {
861 ResolveInfo ri = broadcastReceivers.get(i);
862 addProviderLocked(ri);
863 }
864 }
865
866 boolean addProviderLocked(ResolveInfo ri) {
Joe Onoratod070e892011-01-07 20:50:37 -0800867 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
868 return false;
869 }
870 if (!ri.activityInfo.isEnabled()) {
871 return false;
872 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800873 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
874 ri.activityInfo.name), ri);
875 if (p != null) {
876 mInstalledProviders.add(p);
877 return true;
878 } else {
879 return false;
880 }
881 }
882
883 void removeProviderLocked(int index, Provider p) {
884 int N = p.instances.size();
885 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700886 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700888 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 // Stop telling the host about updates for this from now on
890 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700891 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700893 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 id.provider = null;
895 pruneHostLocked(id.host);
896 id.host = null;
897 }
898 p.instances.clear();
899 mInstalledProviders.remove(index);
900 // no need to send the DISABLE broadcast, since the receiver is gone anyway
901 cancelBroadcasts(p);
902 }
903
904 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700905 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906 intent.setComponent(p.info.provider);
907 mContext.sendBroadcast(intent);
908 }
909
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700910 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
911 if (appWidgetIds != null && appWidgetIds.length > 0) {
912 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
913 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914 intent.setComponent(p.info.provider);
915 mContext.sendBroadcast(intent);
916 }
917 }
918
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700919 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800920 if (p.info.updatePeriodMillis > 0) {
921 // if this is the first instance, set the alarm. otherwise,
922 // rely on the fact that we've already set it and that
923 // PendingIntent.getBroadcast will update the extras.
924 boolean alreadyRegistered = p.broadcast != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700925 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
926 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927 intent.setComponent(p.info.provider);
928 long token = Binder.clearCallingIdentity();
929 try {
930 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
931 PendingIntent.FLAG_UPDATE_CURRENT);
932 } finally {
933 Binder.restoreCallingIdentity(token);
934 }
935 if (!alreadyRegistered) {
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700936 long period = p.info.updatePeriodMillis;
937 if (period < MIN_UPDATE_PERIOD) {
938 period = MIN_UPDATE_PERIOD;
939 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700941 SystemClock.elapsedRealtime() + period, period, p.broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800942 }
943 }
944 }
945
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700946 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700948 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800949 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700950 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800951 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700952 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800953 }
954
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700955 public int[] getAppWidgetIds(ComponentName provider) {
956 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957 Provider p = lookupProviderLocked(provider);
958 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700959 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 } else {
961 return new int[0];
962 }
963 }
964 }
965
966 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
967 Provider p = null;
968
969 ActivityInfo activityInfo = ri.activityInfo;
970 XmlResourceParser parser = null;
971 try {
972 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700973 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800974 if (parser == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800975 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700976 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800977 return null;
978 }
Adam Cohend2e20de2011-02-25 12:03:37 -0800979
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800980 AttributeSet attrs = Xml.asAttributeSet(parser);
Adam Cohend2e20de2011-02-25 12:03:37 -0800981
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800982 int type;
983 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
984 && type != XmlPullParser.START_TAG) {
985 // drain whitespace, comments, etc.
986 }
Adam Cohend2e20de2011-02-25 12:03:37 -0800987
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700989 if (!"appwidget-provider".equals(nodeName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800990 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700991 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800992 return null;
993 }
994
995 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700996 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
Romain Guyd2671e12010-03-11 18:06:42 -0800997 // If metaData was null, we would have returned earlier when getting
998 // the parser No need to do the check here
999 info.oldName = activityInfo.metaData.getString(
1000 AppWidgetManager.META_DATA_APPWIDGET_OLD_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001001
1002 info.provider = component;
1003 p.uid = activityInfo.applicationInfo.uid;
1004
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001005 Resources res = mPackageManager.getResourcesForApplication(
1006 activityInfo.applicationInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001007
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001008 TypedArray sa = res.obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001009 com.android.internal.R.styleable.AppWidgetProviderInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001010
Mitsuru Oshima8f25c422009-07-01 00:10:43 -07001011 // These dimensions has to be resolved in the application's context.
1012 // We simply send back the raw complex data, which will be
1013 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1014 TypedValue value = sa.peekValue(
1015 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1016 info.minWidth = value != null ? value.data : 0;
1017 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1018 info.minHeight = value != null ? value.data : 0;
Adam Cohend2e20de2011-02-25 12:03:37 -08001019
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001021 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001023 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001025 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 if (className != null) {
1027 info.configure = new ComponentName(component.getPackageName(), className);
1028 }
1029 info.label = activityInfo.loadLabel(mPackageManager).toString();
1030 info.icon = ri.getIconResource();
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001031 info.previewImage = sa.getResourceId(
1032 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
Adam Cohena02fdf12010-11-03 13:27:40 -07001033 info.autoAdvanceViewId = sa.getResourceId(
1034 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
Adam Cohen9611f2e2011-02-28 13:39:38 -08001035 info.resizeMode = sa.getInt(
Adam Cohend2e20de2011-02-25 12:03:37 -08001036 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1037 AppWidgetProviderInfo.RESIZE_NONE);
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001038
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 sa.recycle();
1040 } catch (Exception e) {
1041 // Ok to catch Exception here, because anything going wrong because
1042 // of what a client process passes to us should not be fatal for the
1043 // system process.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001044 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 return null;
1046 } finally {
1047 if (parser != null) parser.close();
1048 }
1049 return p;
1050 }
1051
1052 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1053 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
1054 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1055 throw new PackageManager.NameNotFoundException();
1056 }
1057 return pkgInfo.applicationInfo.uid;
1058 }
1059
1060 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1061 int callingUid = getCallingUid();
1062 int packageUid;
1063 try {
1064 packageUid = getUidForPackage(packageName);
1065 } catch (PackageManager.NameNotFoundException ex) {
1066 throw new IllegalArgumentException("packageName and uid don't match packageName="
1067 + packageName);
1068 }
Marco Nelissen54796e72009-04-30 15:16:30 -07001069 if (callingUid != packageUid && Process.supportsProcesses()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 throw new IllegalArgumentException("packageName and uid don't match packageName="
1071 + packageName);
1072 }
1073 return callingUid;
1074 }
1075
1076 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001077 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001078 final int N = mInstalledProviders.size();
1079 for (int i=0; i<N; i++) {
1080 Provider p = mInstalledProviders.get(i);
1081 if (p.instances.size() > 0) {
1082 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001083 int[] appWidgetIds = getAppWidgetIds(p);
1084 sendUpdateIntentLocked(p, appWidgetIds);
1085 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 }
1087 }
1088 }
1089 }
1090
1091 // only call from initialization -- it assumes that the data structures are all empty
1092 void loadStateLocked() {
1093 File temp = savedStateTempFile();
1094 File real = savedStateRealFile();
1095
1096 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
1097 // real one. if there is both a real file and a temp one, assume that the temp one isn't
1098 // fully written and delete it.
1099 if (real.exists()) {
1100 readStateFromFileLocked(real);
1101 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001102 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001103 temp.delete();
1104 }
1105 } else if (temp.exists()) {
1106 readStateFromFileLocked(temp);
Romain Guya5475592009-07-01 17:20:08 -07001107 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001108 temp.renameTo(real);
1109 }
1110 }
1111
1112 void saveStateLocked() {
1113 File temp = savedStateTempFile();
1114 File real = savedStateRealFile();
1115
1116 if (!real.exists()) {
1117 // If the real one doesn't exist, it's either because this is the first time
1118 // or because something went wrong while copying them. In this case, we can't
1119 // trust anything that's in temp. In order to have the loadState code not
1120 // use the temporary one until it's fully written, create an empty file
1121 // for real, which will we'll shortly delete.
1122 try {
Romain Guya5475592009-07-01 17:20:08 -07001123 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001124 real.createNewFile();
1125 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001126 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127 }
1128 }
1129
1130 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001131 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132 temp.delete();
1133 }
1134
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001135 if (!writeStateToFileLocked(temp)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001136 Slog.w(TAG, "Failed to persist new settings");
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001137 return;
1138 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139
Romain Guya5475592009-07-01 17:20:08 -07001140 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001141 real.delete();
Romain Guya5475592009-07-01 17:20:08 -07001142 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001143 temp.renameTo(real);
1144 }
1145
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001146 boolean writeStateToFileLocked(File file) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147 FileOutputStream stream = null;
1148 int N;
1149
1150 try {
1151 stream = new FileOutputStream(file, false);
1152 XmlSerializer out = new FastXmlSerializer();
1153 out.setOutput(stream, "utf-8");
1154 out.startDocument(null, true);
1155
1156
1157 out.startTag(null, "gs");
1158
1159 int providerIndex = 0;
1160 N = mInstalledProviders.size();
1161 for (int i=0; i<N; i++) {
1162 Provider p = mInstalledProviders.get(i);
1163 if (p.instances.size() > 0) {
1164 out.startTag(null, "p");
1165 out.attribute(null, "pkg", p.info.provider.getPackageName());
1166 out.attribute(null, "cl", p.info.provider.getClassName());
Patrick Tsaibd742e432010-05-01 00:30:19 +08001167 out.endTag(null, "p");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001168 p.tag = providerIndex;
1169 providerIndex++;
1170 }
1171 }
1172
1173 N = mHosts.size();
1174 for (int i=0; i<N; i++) {
1175 Host host = mHosts.get(i);
1176 out.startTag(null, "h");
1177 out.attribute(null, "pkg", host.packageName);
1178 out.attribute(null, "id", Integer.toHexString(host.hostId));
1179 out.endTag(null, "h");
1180 host.tag = i;
1181 }
1182
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001183 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001184 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001185 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001186 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001187 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001188 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1189 if (id.provider != null) {
1190 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1191 }
1192 out.endTag(null, "g");
1193 }
1194
1195 out.endTag(null, "gs");
1196
1197 out.endDocument();
1198 stream.close();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001199 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001200 } catch (IOException e) {
1201 try {
1202 if (stream != null) {
1203 stream.close();
1204 }
1205 } catch (IOException ex) {
Romain Guya5475592009-07-01 17:20:08 -07001206 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001207 }
1208 if (file.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001209 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001210 file.delete();
1211 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001212 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001213 }
1214 }
1215
1216 void readStateFromFileLocked(File file) {
1217 FileInputStream stream = null;
1218
1219 boolean success = false;
1220
1221 try {
1222 stream = new FileInputStream(file);
1223 XmlPullParser parser = Xml.newPullParser();
1224 parser.setInput(stream, null);
1225
1226 int type;
1227 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -07001228 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001229 do {
1230 type = parser.next();
1231 if (type == XmlPullParser.START_TAG) {
1232 String tag = parser.getName();
1233 if ("p".equals(tag)) {
1234 // TODO: do we need to check that this package has the same signature
1235 // as before?
1236 String pkg = parser.getAttributeValue(null, "pkg");
1237 String cl = parser.getAttributeValue(null, "cl");
Romain Guyff3e61c62010-03-11 15:30:02 -08001238
1239 final PackageManager packageManager = mContext.getPackageManager();
1240 try {
1241 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1242 } catch (PackageManager.NameNotFoundException e) {
1243 String[] pkgs = packageManager.currentToCanonicalPackageNames(
1244 new String[] { pkg });
1245 pkg = pkgs[0];
1246 }
1247
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001248 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1249 if (p == null && mSafeMode) {
1250 // if we're in safe mode, make a temporary one
1251 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001252 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001253 p.info.provider = new ComponentName(pkg, cl);
1254 p.zombie = true;
1255 mInstalledProviders.add(p);
1256 }
1257 if (p != null) {
1258 // if it wasn't uninstalled or something
1259 loadedProviders.put(providerIndex, p);
1260 }
1261 providerIndex++;
1262 }
1263 else if ("h".equals(tag)) {
1264 Host host = new Host();
1265
1266 // TODO: do we need to check that this package has the same signature
1267 // as before?
1268 host.packageName = parser.getAttributeValue(null, "pkg");
1269 try {
1270 host.uid = getUidForPackage(host.packageName);
1271 } catch (PackageManager.NameNotFoundException ex) {
1272 host.zombie = true;
1273 }
1274 if (!host.zombie || mSafeMode) {
1275 // In safe mode, we don't discard the hosts we don't recognize
1276 // so that they're not pruned from our list. Otherwise, we do.
1277 host.hostId = Integer.parseInt(
1278 parser.getAttributeValue(null, "id"), 16);
1279 mHosts.add(host);
1280 }
1281 }
1282 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001283 AppWidgetId id = new AppWidgetId();
1284 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1285 if (id.appWidgetId >= mNextAppWidgetId) {
1286 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001287 }
1288
1289 String providerString = parser.getAttributeValue(null, "p");
1290 if (providerString != null) {
1291 // there's no provider if it hasn't been bound yet.
1292 // maybe we don't have to save this, but it brings the system
1293 // to the state it was in.
1294 int pIndex = Integer.parseInt(providerString, 16);
1295 id.provider = loadedProviders.get(pIndex);
1296 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001297 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001298 + pIndex + " which is " + id.provider);
1299 }
1300 if (id.provider == null) {
1301 // This provider is gone. We just let the host figure out
1302 // that this happened when it fails to load it.
1303 continue;
1304 }
1305 }
1306
1307 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1308 id.host = mHosts.get(hIndex);
1309 if (id.host == null) {
1310 // This host is gone.
1311 continue;
1312 }
1313
1314 if (id.provider != null) {
1315 id.provider.instances.add(id);
1316 }
1317 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001318 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001319 }
1320 }
1321 } while (type != XmlPullParser.END_DOCUMENT);
1322 success = true;
1323 } catch (NullPointerException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001324 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001325 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001326 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 } catch (XmlPullParserException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001328 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001329 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001330 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001331 } catch (IndexOutOfBoundsException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001332 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001333 }
1334 try {
1335 if (stream != null) {
1336 stream.close();
1337 }
1338 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001339 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001340 }
1341
1342 if (success) {
1343 // delete any hosts that didn't manage to get connected (should happen)
1344 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001345 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001346 pruneHostLocked(mHosts.get(i));
1347 }
1348 } else {
1349 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001350 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001351 mHosts.clear();
1352 final int N = mInstalledProviders.size();
1353 for (int i=0; i<N; i++) {
1354 mInstalledProviders.get(i).instances.clear();
1355 }
1356 }
1357 }
1358
1359 File savedStateTempFile() {
1360 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1361 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1362 }
1363
1364 File savedStateRealFile() {
1365 return new File("/data/system/" + SETTINGS_FILENAME);
1366 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1367 }
1368
1369 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1370 public void onReceive(Context context, Intent intent) {
1371 String action = intent.getAction();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001372 //Slog.d(TAG, "received " + action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001373 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1374 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001375 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1376 Locale revised = Locale.getDefault();
1377 if (revised == null || mLocale == null ||
1378 !(revised.equals(mLocale))) {
1379 mLocale = revised;
1380
1381 synchronized (mAppWidgetIds) {
1382 int N = mInstalledProviders.size();
1383 for (int i=N-1; i>=0; i--) {
1384 Provider p = mInstalledProviders.get(i);
1385 String pkgName = p.info.provider.getPackageName();
1386 updateProvidersForPackageLocked(pkgName);
1387 }
1388 saveStateLocked();
1389 }
1390 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001391 } else {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001392 boolean added = false;
Joe Onoratod070e892011-01-07 20:50:37 -08001393 boolean changed = false;
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001394 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001395 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001396 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1397 added = true;
Joe Onorato94258cd2010-08-24 16:45:40 -04001398 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001399 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1400 added = false;
1401 } else {
1402 Uri uri = intent.getData();
1403 if (uri == null) {
1404 return;
1405 }
1406 String pkgName = uri.getSchemeSpecificPart();
1407 if (pkgName == null) {
1408 return;
1409 }
1410 pkgList = new String[] { pkgName };
1411 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
Joe Onoratod070e892011-01-07 20:50:37 -08001412 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001413 }
1414 if (pkgList == null || pkgList.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001415 return;
1416 }
Joe Onoratod070e892011-01-07 20:50:37 -08001417 if (added || changed) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001418 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001419 Bundle extras = intent.getExtras();
Joe Onoratod070e892011-01-07 20:50:37 -08001420 if (changed || (extras != null &&
1421 extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001422 for (String pkgName : pkgList) {
1423 // The package was just upgraded
1424 updateProvidersForPackageLocked(pkgName);
1425 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001426 } else {
1427 // The package was just added
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001428 for (String pkgName : pkgList) {
1429 addProvidersForPackageLocked(pkgName);
1430 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001431 }
1432 saveStateLocked();
1433 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001434 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001435 Bundle extras = intent.getExtras();
1436 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1437 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1438 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001439 synchronized (mAppWidgetIds) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001440 for (String pkgName : pkgList) {
1441 removeProvidersForPackageLocked(pkgName);
1442 saveStateLocked();
1443 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001444 }
1445 }
1446 }
1447 }
1448 }
1449 };
1450
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001451 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001452 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001453 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001454 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1455 PackageManager.GET_META_DATA);
1456
Bjorn Bringert5f857802010-02-10 23:09:48 +00001457 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001458 for (int i=0; i<N; i++) {
1459 ResolveInfo ri = broadcastReceivers.get(i);
1460 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001461 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1462 continue;
1463 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001464 if (pkgName.equals(ai.packageName)) {
1465 addProviderLocked(ri);
1466 }
1467 }
1468 }
1469
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001470 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001471 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001472 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001473 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001474 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1475 PackageManager.GET_META_DATA);
1476
1477 // add the missing ones and collect which ones to keep
Bjorn Bringert5f857802010-02-10 23:09:48 +00001478 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001479 for (int i=0; i<N; i++) {
1480 ResolveInfo ri = broadcastReceivers.get(i);
1481 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001482 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1483 continue;
1484 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 if (pkgName.equals(ai.packageName)) {
1486 ComponentName component = new ComponentName(ai.packageName, ai.name);
1487 Provider p = lookupProviderLocked(component);
1488 if (p == null) {
1489 if (addProviderLocked(ri)) {
1490 keep.add(ai.name);
1491 }
1492 } else {
1493 Provider parsed = parseProviderInfoXml(component, ri);
1494 if (parsed != null) {
1495 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001496 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001497 p.info = parsed.info;
1498 // If it's enabled
1499 final int M = p.instances.size();
1500 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001501 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001502 // Reschedule for the new updatePeriodMillis (don't worry about handling
1503 // it specially if updatePeriodMillis didn't change because we just sent
1504 // an update, and the next one will be updatePeriodMillis from now).
1505 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001506 registerForBroadcastsLocked(p, appWidgetIds);
1507 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001508 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001509 AppWidgetId id = p.instances.get(j);
Joe Onoratoa8a8a422010-06-16 15:06:16 -04001510 id.views = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001511 if (id.host != null && id.host.callbacks != null) {
1512 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001513 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001514 } catch (RemoteException ex) {
1515 // It failed; remove the callback. No need to prune because
1516 // we know that this host is still referenced by this
1517 // instance.
1518 id.host.callbacks = null;
1519 }
1520 }
1521 }
1522 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001523 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001524 }
1525 }
1526 }
1527 }
1528 }
1529
1530 // prune the ones we don't want to keep
1531 N = mInstalledProviders.size();
1532 for (int i=N-1; i>=0; i--) {
1533 Provider p = mInstalledProviders.get(i);
1534 if (pkgName.equals(p.info.provider.getPackageName())
1535 && !keep.contains(p.info.provider.getClassName())) {
1536 removeProviderLocked(i, p);
1537 }
1538 }
1539 }
1540
1541 void removeProvidersForPackageLocked(String pkgName) {
1542 int N = mInstalledProviders.size();
1543 for (int i=N-1; i>=0; i--) {
1544 Provider p = mInstalledProviders.get(i);
1545 if (pkgName.equals(p.info.provider.getPackageName())) {
1546 removeProviderLocked(i, p);
1547 }
1548 }
1549
1550 // Delete the hosts for this package too
1551 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001552 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001553 // so we don't need to worry about sending DISABLE broadcasts to them.
1554 N = mHosts.size();
1555 for (int i=N-1; i>=0; i--) {
1556 Host host = mHosts.get(i);
1557 if (pkgName.equals(host.packageName)) {
1558 deleteHostLocked(host);
1559 }
1560 }
1561 }
1562}
1563