blob: a679ca72daa201b207415dae604ab29a39ee7ec7 [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);
Adam Cohen324afba2011-07-22 11:51:45 -0700233 pw.print(") minResize=("); pw.print(info.minResizeWidth);
234 pw.print("x"); pw.print(info.minResizeHeight);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700235 pw.print(") updatePeriodMillis=");
236 pw.print(info.updatePeriodMillis);
Adam Cohen324afba2011-07-22 11:51:45 -0700237 pw.print(" resizeMode=");
238 pw.print(info.resizeMode);
239 pw.print(" autoAdvanceViewId=");
240 pw.print(info.autoAdvanceViewId);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700241 pw.print(" initialLayout=#");
242 pw.print(Integer.toHexString(info.initialLayout));
243 pw.print(" zombie="); pw.println(p.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 }
245
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700246 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700247 pw.println(" ");
248 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700250 AppWidgetId id = mAppWidgetIds.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700251 pw.print(" ["); pw.print(i); pw.print("] id=");
Romain Guya5475592009-07-01 17:20:08 -0700252 pw.println(id.appWidgetId);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700253 pw.print(" hostId=");
254 pw.print(id.host.hostId); pw.print(' ');
255 pw.print(id.host.packageName); pw.print('/');
256 pw.println(id.host.uid);
257 if (id.provider != null) {
258 pw.print(" provider=");
259 pw.println(id.provider.info.provider.flattenToShortString());
260 }
261 if (id.host != null) {
262 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
263 }
264 if (id.views != null) {
265 pw.print(" views="); pw.println(id.views);
266 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 }
268
269 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700270 pw.println(" ");
271 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 for (int i=0; i<N; i++) {
273 Host host = mHosts.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700274 pw.print(" ["); pw.print(i); pw.print("] hostId=");
275 pw.print(host.hostId); pw.print(' ');
276 pw.print(host.packageName); pw.print('/');
277 pw.print(host.uid); pw.println(':');
278 pw.print(" callbacks="); pw.println(host.callbacks);
279 pw.print(" instances.size="); pw.print(host.instances.size());
280 pw.print(" zombie="); pw.println(host.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 }
282 }
283 }
284
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700285 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700287 synchronized (mAppWidgetIds) {
288 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289
290 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
291
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700292 AppWidgetId id = new AppWidgetId();
293 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 id.host = host;
295
296 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700297 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298
299 saveStateLocked();
300
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700301 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 }
303 }
304
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700305 public void deleteAppWidgetId(int appWidgetId) {
306 synchronized (mAppWidgetIds) {
307 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700309 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 saveStateLocked();
311 }
312 }
313 }
314
315 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700316 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 int callingUid = getCallingUid();
318 Host host = lookupHostLocked(callingUid, hostId);
319 if (host != null) {
320 deleteHostLocked(host);
321 saveStateLocked();
322 }
323 }
324 }
325
326 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700327 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 int callingUid = getCallingUid();
329 final int N = mHosts.size();
330 boolean changed = false;
331 for (int i=N-1; i>=0; i--) {
332 Host host = mHosts.get(i);
333 if (host.uid == callingUid) {
334 deleteHostLocked(host);
335 changed = true;
336 }
337 }
338 if (changed) {
339 saveStateLocked();
340 }
341 }
342 }
343
344 void deleteHostLocked(Host host) {
345 final int N = host.instances.size();
346 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700347 AppWidgetId id = host.instances.get(i);
348 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 }
350 host.instances.clear();
351 mHosts.remove(host);
352 // it's gone or going away, abruptly drop the callback connection
353 host.callbacks = null;
354 }
355
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700356 void deleteAppWidgetLocked(AppWidgetId id) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800357 // We first unbind all services that are bound to this id
358 unbindAppWidgetRemoteViewsServicesLocked(id);
359
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 Host host = id.host;
361 host.instances.remove(id);
362 pruneHostLocked(host);
363
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700364 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800365
366 Provider p = id.provider;
367 if (p != null) {
368 p.instances.remove(id);
369 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700370 // send the broacast saying that this appWidgetId has been deleted
371 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700373 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 mContext.sendBroadcast(intent);
375 if (p.instances.size() == 0) {
376 // cancel the future updates
377 cancelBroadcasts(p);
378
379 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700380 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 intent.setComponent(p.info.provider);
382 mContext.sendBroadcast(intent);
383 }
384 }
385 }
386 }
387
388 void cancelBroadcasts(Provider p) {
389 if (p.broadcast != null) {
390 mAlarmManager.cancel(p.broadcast);
391 long token = Binder.clearCallingIdentity();
392 try {
393 p.broadcast.cancel();
394 } finally {
395 Binder.restoreCallingIdentity(token);
396 }
397 p.broadcast = null;
398 }
399 }
400
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700401 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
402 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
403 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
Dianne Hackborn4912f162011-06-15 17:40:44 -0700404
405 final long ident = Binder.clearCallingIdentity();
406 try {
407 synchronized (mAppWidgetIds) {
408 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
409 if (id == null) {
410 throw new IllegalArgumentException("bad appWidgetId");
411 }
412 if (id.provider != null) {
413 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
414 + id.provider.info.provider);
415 }
416 Provider p = lookupProviderLocked(provider);
417 if (p == null) {
418 throw new IllegalArgumentException("not a appwidget provider: " + provider);
419 }
420 if (p.zombie) {
421 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
422 + " safe mode: " + provider);
423 }
424
425 id.provider = p;
426 p.instances.add(id);
427 int instancesSize = p.instances.size();
428 if (instancesSize == 1) {
429 // tell the provider that it's ready
430 sendEnableIntentLocked(p);
431 }
432
433 // send an update now -- We need this update now, and just for this appWidgetId.
434 // It's less critical when the next one happens, so when we schdule the next one,
435 // we add updatePeriodMillis to its start time. That time will have some slop,
436 // but that's okay.
437 sendUpdateIntentLocked(p, new int[] { appWidgetId });
438
439 // schedule the future updates
440 registerForBroadcastsLocked(p, getAppWidgetIds(p));
441 saveStateLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 }
Dianne Hackborn4912f162011-06-15 17:40:44 -0700443 } finally {
444 Binder.restoreCallingIdentity(ident);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 }
446 }
447
Winson Chung84bbb022011-02-21 13:57:45 -0800448 // Binds to a specific RemoteViewsService
Winson Chung81f39eb2011-01-11 18:05:01 -0800449 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
450 synchronized (mAppWidgetIds) {
451 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
452 if (id == null) {
453 throw new IllegalArgumentException("bad appWidgetId");
454 }
455 final ComponentName componentName = intent.getComponent();
456 try {
457 final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
458 PackageManager.GET_PERMISSIONS);
459 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
460 throw new SecurityException("Selected service does not require "
461 + android.Manifest.permission.BIND_REMOTEVIEWS
462 + ": " + componentName);
463 }
464 } catch (PackageManager.NameNotFoundException e) {
465 throw new IllegalArgumentException("Unknown component " + componentName);
466 }
467
Winson Chung16c8d8a2011-01-20 16:19:33 -0800468 // If there is already a connection made for this service intent, then disconnect from
469 // that first. (This does not allow multiple connections to the same service under
470 // the same key)
471 ServiceConnectionProxy conn = null;
Winson Chung84bbb022011-02-21 13:57:45 -0800472 FilterComparison fc = new FilterComparison(intent);
473 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800474 if (mBoundRemoteViewsServices.containsKey(key)) {
475 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
476 conn.disconnect();
477 mContext.unbindService(conn);
478 mBoundRemoteViewsServices.remove(key);
479 }
480
481 // Bind to the RemoteViewsService (which will trigger a callback to the
482 // RemoteViewsAdapter.onServiceConnected())
Winson Chung81f39eb2011-01-11 18:05:01 -0800483 final long token = Binder.clearCallingIdentity();
484 try {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800485 conn = new ServiceConnectionProxy(key, connection);
Winson Chung81f39eb2011-01-11 18:05:01 -0800486 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
487 mBoundRemoteViewsServices.put(key, conn);
488 } finally {
489 Binder.restoreCallingIdentity(token);
490 }
Winson Chung84bbb022011-02-21 13:57:45 -0800491
492 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
493 // when we can call back to the RemoteViewsService later to destroy associated
494 // factories.
Winson Chung22bc69d2011-02-25 15:13:38 -0800495 incrementAppWidgetServiceRefCount(appWidgetId, fc);
Winson Chung81f39eb2011-01-11 18:05:01 -0800496 }
497 }
498
Winson Chung84bbb022011-02-21 13:57:45 -0800499 // Unbinds from a specific RemoteViewsService
Winson Chung81f39eb2011-01-11 18:05:01 -0800500 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
501 synchronized (mAppWidgetIds) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800502 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
503 // RemoteViewsAdapter)
504 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
505 new FilterComparison(intent));
506 if (mBoundRemoteViewsServices.containsKey(key)) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800507 // We don't need to use the appWidgetId until after we are sure there is something
508 // to unbind. Note that this may mask certain issues with apps calling unbind()
509 // more than necessary.
510 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
511 if (id == null) {
512 throw new IllegalArgumentException("bad appWidgetId");
513 }
514
515 ServiceConnectionProxy conn =
516 (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
517 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800518 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800519 mBoundRemoteViewsServices.remove(key);
520 } else {
521 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
Winson Chung81f39eb2011-01-11 18:05:01 -0800522 }
523 }
524 }
525
Winson Chung84bbb022011-02-21 13:57:45 -0800526 // Unbinds from a RemoteViewsService when we delete an app widget
Winson Chung81f39eb2011-01-11 18:05:01 -0800527 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800528 int appWidgetId = id.appWidgetId;
529 // Unbind all connections to Services bound to this AppWidgetId
Winson Chung81f39eb2011-01-11 18:05:01 -0800530 Iterator<Pair<Integer, Intent.FilterComparison>> it =
531 mBoundRemoteViewsServices.keySet().iterator();
Winson Chung81f39eb2011-01-11 18:05:01 -0800532 while (it.hasNext()) {
533 final Pair<Integer, Intent.FilterComparison> key = it.next();
534 if (key.first.intValue() == appWidgetId) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800535 final ServiceConnectionProxy conn = (ServiceConnectionProxy)
536 mBoundRemoteViewsServices.get(key);
537 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800538 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800539 it.remove();
Winson Chung81f39eb2011-01-11 18:05:01 -0800540 }
541 }
Winson Chung84bbb022011-02-21 13:57:45 -0800542
543 // Check if we need to destroy any services (if no other app widgets are
544 // referencing the same service)
545 decrementAppWidgetServiceRefCount(appWidgetId);
546 }
547
548 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
549 private void destroyRemoteViewsService(final Intent intent) {
550 final ServiceConnection conn = new ServiceConnection() {
551 @Override
552 public void onServiceConnected(ComponentName name, IBinder service) {
553 final IRemoteViewsFactory cb =
554 IRemoteViewsFactory.Stub.asInterface(service);
555 try {
556 cb.onDestroy(intent);
Adam Cohen2625fea2011-03-23 17:24:30 -0700557 } catch (RemoteException e) {
558 e.printStackTrace();
559 } catch (RuntimeException e) {
Winson Chung84bbb022011-02-21 13:57:45 -0800560 e.printStackTrace();
561 }
562 mContext.unbindService(this);
563 }
564 @Override
565 public void onServiceDisconnected(android.content.ComponentName name) {
566 // Do nothing
567 }
568 };
569
570 // Bind to the service and remove the static intent->factory mapping in the
571 // RemoteViewsService.
572 final long token = Binder.clearCallingIdentity();
573 try {
574 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
575 } finally {
576 Binder.restoreCallingIdentity(token);
577 }
578 }
579
580 // Adds to the ref-count for a given RemoteViewsService intent
581 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
582 HashSet<Integer> appWidgetIds = null;
583 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
584 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
585 } else {
586 appWidgetIds = new HashSet<Integer>();
587 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
588 }
589 appWidgetIds.add(appWidgetId);
590 }
591
592 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
593 // the ref-count reaches zero.
594 private void decrementAppWidgetServiceRefCount(int appWidgetId) {
595 Iterator<FilterComparison> it =
596 mRemoteViewsServicesAppWidgets.keySet().iterator();
597 while (it.hasNext()) {
598 final FilterComparison key = it.next();
599 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
600 if (ids.remove(appWidgetId)) {
601 // If we have removed the last app widget referencing this service, then we
602 // should destroy it and remove it from this set
603 if (ids.isEmpty()) {
604 destroyRemoteViewsService(key.getIntent());
605 it.remove();
606 }
607 }
608 }
Winson Chung81f39eb2011-01-11 18:05:01 -0800609 }
610
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700611 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
612 synchronized (mAppWidgetIds) {
613 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 if (id != null && id.provider != null && !id.provider.zombie) {
615 return id.provider.info;
616 }
617 return null;
618 }
619 }
620
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700621 public RemoteViews getAppWidgetViews(int appWidgetId) {
622 synchronized (mAppWidgetIds) {
623 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 if (id != null) {
625 return id.views;
626 }
627 return null;
628 }
629 }
630
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700631 public List<AppWidgetProviderInfo> getInstalledProviders() {
632 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800633 final int N = mInstalledProviders.size();
Romain Guya5475592009-07-01 17:20:08 -0700634 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800635 for (int i=0; i<N; i++) {
636 Provider p = mInstalledProviders.get(i);
637 if (!p.zombie) {
638 result.add(p.info);
639 }
640 }
641 return result;
642 }
643 }
644
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700645 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
646 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647 return;
648 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700649 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800650 return;
651 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700652 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700654 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800655 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700656 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
657 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 }
659 }
660 }
661
Adam Cohen2dd21972010-08-15 18:20:04 -0700662 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
663 if (appWidgetIds == null) {
664 return;
665 }
666 if (appWidgetIds.length == 0) {
667 return;
668 }
669 final int N = appWidgetIds.length;
670
671 synchronized (mAppWidgetIds) {
672 for (int i=0; i<N; i++) {
673 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
674 updateAppWidgetInstanceLocked(id, views, true);
675 }
676 }
677 }
678
Winson Chung6394c0e2010-08-16 10:14:56 -0700679 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700680 if (appWidgetIds == null) {
681 return;
682 }
683 if (appWidgetIds.length == 0) {
684 return;
685 }
686 final int N = appWidgetIds.length;
687
688 synchronized (mAppWidgetIds) {
689 for (int i=0; i<N; i++) {
690 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Winson Chung6394c0e2010-08-16 10:14:56 -0700691 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700692 }
693 }
694 }
695
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700696 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
697 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698 Provider p = lookupProviderLocked(provider);
699 if (p == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800700 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701 return;
702 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700703 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800704 final int N = instances.size();
705 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700706 AppWidgetId id = instances.get(i);
707 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 }
709 }
710 }
711
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700712 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700713 updateAppWidgetInstanceLocked(id, views, false);
714 }
715
716 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700717 // allow for stale appWidgetIds and other badness
718 // lookup also checks that the calling process can access the appWidgetId
719 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800720 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700721
722 // We do not want to save this RemoteViews
723 if (!isPartialUpdate) id.views = views;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800724
725 // is anyone listening?
726 if (id.host.callbacks != null) {
727 try {
728 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700729 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800730 } catch (RemoteException e) {
731 // It failed; remove the callback. No need to prune because
732 // we know that this host is still referenced by this instance.
733 id.host.callbacks = null;
734 }
735 }
736 }
737 }
738
Winson Chung6394c0e2010-08-16 10:14:56 -0700739 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700740 // allow for stale appWidgetIds and other badness
741 // lookup also checks that the calling process can access the appWidgetId
742 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
743 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700744 // is anyone listening?
745 if (id.host.callbacks != null) {
746 try {
747 // the lock is held, but this is a oneway call
Winson Chung6394c0e2010-08-16 10:14:56 -0700748 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700749 } catch (RemoteException e) {
750 // It failed; remove the callback. No need to prune because
751 // we know that this host is still referenced by this instance.
752 id.host.callbacks = null;
753 }
754 }
755 }
756 }
757
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700758 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 List<RemoteViews> updatedViews) {
760 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700761 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
763 host.callbacks = callbacks;
764
765 updatedViews.clear();
766
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700767 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 int N = instances.size();
769 int[] updatedIds = new int[N];
770 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700771 AppWidgetId id = instances.get(i);
772 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773 updatedViews.add(id.views);
774 }
775 return updatedIds;
776 }
777 }
778
779 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700780 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800781 Host host = lookupHostLocked(getCallingUid(), hostId);
Ken Shirriffe21167a2009-09-23 16:42:53 -0700782 if (host != null) {
783 host.callbacks = null;
784 pruneHostLocked(host);
785 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 }
787 }
788
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700789 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800790 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700791 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792 return true;
793 }
794 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700795 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 return true;
797 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700798 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700800 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800801 return true;
802 }
803 // Nobody else can access it.
804 return false;
805 }
806
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700807 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700809 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700811 AppWidgetId id = mAppWidgetIds.get(i);
812 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 return id;
814 }
815 }
816 return null;
817 }
818
819 Provider lookupProviderLocked(ComponentName provider) {
Romain Guyd2671e12010-03-11 18:06:42 -0800820 final String className = provider.getClassName();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800821 final int N = mInstalledProviders.size();
822 for (int i=0; i<N; i++) {
823 Provider p = mInstalledProviders.get(i);
Romain Guyd2671e12010-03-11 18:06:42 -0800824 if (p.info.provider.equals(provider) || className.equals(p.info.oldName)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 return p;
826 }
827 }
828 return null;
829 }
830
831 Host lookupHostLocked(int uid, int hostId) {
832 final int N = mHosts.size();
833 for (int i=0; i<N; i++) {
834 Host h = mHosts.get(i);
835 if (h.uid == uid && h.hostId == hostId) {
836 return h;
837 }
838 }
839 return null;
840 }
841
842 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
843 final int N = mHosts.size();
844 for (int i=0; i<N; i++) {
845 Host h = mHosts.get(i);
846 if (h.hostId == hostId && h.packageName.equals(packageName)) {
847 return h;
848 }
849 }
850 Host host = new Host();
851 host.packageName = packageName;
852 host.uid = uid;
853 host.hostId = hostId;
854 mHosts.add(host);
855 return host;
856 }
857
858 void pruneHostLocked(Host host) {
859 if (host.instances.size() == 0 && host.callbacks == null) {
860 mHosts.remove(host);
861 }
862 }
863
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700864 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865 PackageManager pm = mPackageManager;
866
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700867 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
869 PackageManager.GET_META_DATA);
870
Bjorn Bringert5f857802010-02-10 23:09:48 +0000871 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 for (int i=0; i<N; i++) {
873 ResolveInfo ri = broadcastReceivers.get(i);
874 addProviderLocked(ri);
875 }
876 }
877
878 boolean addProviderLocked(ResolveInfo ri) {
Joe Onoratod070e892011-01-07 20:50:37 -0800879 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
880 return false;
881 }
882 if (!ri.activityInfo.isEnabled()) {
883 return false;
884 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800885 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
886 ri.activityInfo.name), ri);
887 if (p != null) {
888 mInstalledProviders.add(p);
889 return true;
890 } else {
891 return false;
892 }
893 }
894
895 void removeProviderLocked(int index, Provider p) {
896 int N = p.instances.size();
897 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700898 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700900 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800901 // Stop telling the host about updates for this from now on
902 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700903 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700905 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906 id.provider = null;
907 pruneHostLocked(id.host);
908 id.host = null;
909 }
910 p.instances.clear();
911 mInstalledProviders.remove(index);
912 // no need to send the DISABLE broadcast, since the receiver is gone anyway
913 cancelBroadcasts(p);
914 }
915
916 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700917 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 intent.setComponent(p.info.provider);
919 mContext.sendBroadcast(intent);
920 }
921
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700922 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
923 if (appWidgetIds != null && appWidgetIds.length > 0) {
924 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
925 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800926 intent.setComponent(p.info.provider);
927 mContext.sendBroadcast(intent);
928 }
929 }
930
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700931 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800932 if (p.info.updatePeriodMillis > 0) {
933 // if this is the first instance, set the alarm. otherwise,
934 // rely on the fact that we've already set it and that
935 // PendingIntent.getBroadcast will update the extras.
936 boolean alreadyRegistered = p.broadcast != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700937 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
938 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800939 intent.setComponent(p.info.provider);
940 long token = Binder.clearCallingIdentity();
941 try {
942 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
943 PendingIntent.FLAG_UPDATE_CURRENT);
944 } finally {
945 Binder.restoreCallingIdentity(token);
946 }
947 if (!alreadyRegistered) {
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700948 long period = p.info.updatePeriodMillis;
949 if (period < MIN_UPDATE_PERIOD) {
950 period = MIN_UPDATE_PERIOD;
951 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800952 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700953 SystemClock.elapsedRealtime() + period, period, p.broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 }
955 }
956 }
957
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700958 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800959 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700960 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800961 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700962 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800963 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700964 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800965 }
966
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700967 public int[] getAppWidgetIds(ComponentName provider) {
968 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800969 Provider p = lookupProviderLocked(provider);
970 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700971 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 } else {
973 return new int[0];
974 }
975 }
976 }
977
978 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
979 Provider p = null;
980
981 ActivityInfo activityInfo = ri.activityInfo;
982 XmlResourceParser parser = null;
983 try {
984 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700985 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986 if (parser == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800987 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700988 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800989 return null;
990 }
Adam Cohend2e20de2011-02-25 12:03:37 -0800991
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800992 AttributeSet attrs = Xml.asAttributeSet(parser);
Adam Cohend2e20de2011-02-25 12:03:37 -0800993
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 int type;
995 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
996 && type != XmlPullParser.START_TAG) {
997 // drain whitespace, comments, etc.
998 }
Adam Cohend2e20de2011-02-25 12:03:37 -0800999
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001001 if (!"appwidget-provider".equals(nodeName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001002 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001003 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 return null;
1005 }
1006
1007 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001008 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
Romain Guyd2671e12010-03-11 18:06:42 -08001009 // If metaData was null, we would have returned earlier when getting
1010 // the parser No need to do the check here
1011 info.oldName = activityInfo.metaData.getString(
1012 AppWidgetManager.META_DATA_APPWIDGET_OLD_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013
1014 info.provider = component;
1015 p.uid = activityInfo.applicationInfo.uid;
1016
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001017 Resources res = mPackageManager.getResourcesForApplication(
1018 activityInfo.applicationInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001019
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001020 TypedArray sa = res.obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001021 com.android.internal.R.styleable.AppWidgetProviderInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001022
Mitsuru Oshima8f25c422009-07-01 00:10:43 -07001023 // These dimensions has to be resolved in the application's context.
1024 // We simply send back the raw complex data, which will be
1025 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1026 TypedValue value = sa.peekValue(
1027 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1028 info.minWidth = value != null ? value.data : 0;
1029 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1030 info.minHeight = value != null ? value.data : 0;
Adam Cohen1bfaf562011-07-19 18:05:33 -07001031 value = sa.peekValue(
1032 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1033 info.minResizeWidth = value != null ? value.data : info.minWidth;
1034 value = sa.peekValue(
1035 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1036 info.minResizeHeight = value != null ? value.data : info.minHeight;
Adam Cohend2e20de2011-02-25 12:03:37 -08001037
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001039 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001040 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001041 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001043 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 if (className != null) {
1045 info.configure = new ComponentName(component.getPackageName(), className);
1046 }
1047 info.label = activityInfo.loadLabel(mPackageManager).toString();
1048 info.icon = ri.getIconResource();
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001049 info.previewImage = sa.getResourceId(
1050 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
Adam Cohena02fdf12010-11-03 13:27:40 -07001051 info.autoAdvanceViewId = sa.getResourceId(
1052 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
Adam Cohen9611f2e2011-02-28 13:39:38 -08001053 info.resizeMode = sa.getInt(
Adam Cohend2e20de2011-02-25 12:03:37 -08001054 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1055 AppWidgetProviderInfo.RESIZE_NONE);
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001056
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 sa.recycle();
1058 } catch (Exception e) {
1059 // Ok to catch Exception here, because anything going wrong because
1060 // of what a client process passes to us should not be fatal for the
1061 // system process.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001062 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063 return null;
1064 } finally {
1065 if (parser != null) parser.close();
1066 }
1067 return p;
1068 }
1069
1070 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1071 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
1072 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1073 throw new PackageManager.NameNotFoundException();
1074 }
1075 return pkgInfo.applicationInfo.uid;
1076 }
1077
1078 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1079 int callingUid = getCallingUid();
1080 int packageUid;
1081 try {
1082 packageUid = getUidForPackage(packageName);
1083 } catch (PackageManager.NameNotFoundException ex) {
1084 throw new IllegalArgumentException("packageName and uid don't match packageName="
1085 + packageName);
1086 }
Jeff Brown10e89712011-07-08 18:52:57 -07001087 if (callingUid != packageUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001088 throw new IllegalArgumentException("packageName and uid don't match packageName="
1089 + packageName);
1090 }
1091 return callingUid;
1092 }
1093
1094 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001095 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001096 final int N = mInstalledProviders.size();
1097 for (int i=0; i<N; i++) {
1098 Provider p = mInstalledProviders.get(i);
1099 if (p.instances.size() > 0) {
1100 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001101 int[] appWidgetIds = getAppWidgetIds(p);
1102 sendUpdateIntentLocked(p, appWidgetIds);
1103 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104 }
1105 }
1106 }
1107 }
1108
1109 // only call from initialization -- it assumes that the data structures are all empty
1110 void loadStateLocked() {
1111 File temp = savedStateTempFile();
1112 File real = savedStateRealFile();
1113
1114 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
1115 // real one. if there is both a real file and a temp one, assume that the temp one isn't
1116 // fully written and delete it.
1117 if (real.exists()) {
1118 readStateFromFileLocked(real);
1119 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001120 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001121 temp.delete();
1122 }
1123 } else if (temp.exists()) {
1124 readStateFromFileLocked(temp);
Romain Guya5475592009-07-01 17:20:08 -07001125 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 temp.renameTo(real);
1127 }
1128 }
1129
1130 void saveStateLocked() {
1131 File temp = savedStateTempFile();
1132 File real = savedStateRealFile();
1133
1134 if (!real.exists()) {
1135 // If the real one doesn't exist, it's either because this is the first time
1136 // or because something went wrong while copying them. In this case, we can't
1137 // trust anything that's in temp. In order to have the loadState code not
1138 // use the temporary one until it's fully written, create an empty file
1139 // for real, which will we'll shortly delete.
1140 try {
Romain Guya5475592009-07-01 17:20:08 -07001141 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142 real.createNewFile();
1143 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001144 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001145 }
1146 }
1147
1148 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001149 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 temp.delete();
1151 }
1152
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001153 if (!writeStateToFileLocked(temp)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001154 Slog.w(TAG, "Failed to persist new settings");
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001155 return;
1156 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001157
Romain Guya5475592009-07-01 17:20:08 -07001158 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001159 real.delete();
Romain Guya5475592009-07-01 17:20:08 -07001160 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001161 temp.renameTo(real);
1162 }
1163
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001164 boolean writeStateToFileLocked(File file) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001165 FileOutputStream stream = null;
1166 int N;
1167
1168 try {
1169 stream = new FileOutputStream(file, false);
1170 XmlSerializer out = new FastXmlSerializer();
1171 out.setOutput(stream, "utf-8");
1172 out.startDocument(null, true);
1173
1174
1175 out.startTag(null, "gs");
1176
1177 int providerIndex = 0;
1178 N = mInstalledProviders.size();
1179 for (int i=0; i<N; i++) {
1180 Provider p = mInstalledProviders.get(i);
1181 if (p.instances.size() > 0) {
1182 out.startTag(null, "p");
1183 out.attribute(null, "pkg", p.info.provider.getPackageName());
1184 out.attribute(null, "cl", p.info.provider.getClassName());
Patrick Tsaibd742e432010-05-01 00:30:19 +08001185 out.endTag(null, "p");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001186 p.tag = providerIndex;
1187 providerIndex++;
1188 }
1189 }
1190
1191 N = mHosts.size();
1192 for (int i=0; i<N; i++) {
1193 Host host = mHosts.get(i);
1194 out.startTag(null, "h");
1195 out.attribute(null, "pkg", host.packageName);
1196 out.attribute(null, "id", Integer.toHexString(host.hostId));
1197 out.endTag(null, "h");
1198 host.tag = i;
1199 }
1200
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001201 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001202 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001203 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001204 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001205 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001206 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1207 if (id.provider != null) {
1208 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1209 }
1210 out.endTag(null, "g");
1211 }
1212
1213 out.endTag(null, "gs");
1214
1215 out.endDocument();
1216 stream.close();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001217 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001218 } catch (IOException e) {
1219 try {
1220 if (stream != null) {
1221 stream.close();
1222 }
1223 } catch (IOException ex) {
Romain Guya5475592009-07-01 17:20:08 -07001224 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001225 }
1226 if (file.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001227 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228 file.delete();
1229 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001230 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001231 }
1232 }
1233
1234 void readStateFromFileLocked(File file) {
1235 FileInputStream stream = null;
1236
1237 boolean success = false;
1238
1239 try {
1240 stream = new FileInputStream(file);
1241 XmlPullParser parser = Xml.newPullParser();
1242 parser.setInput(stream, null);
1243
1244 int type;
1245 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -07001246 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 do {
1248 type = parser.next();
1249 if (type == XmlPullParser.START_TAG) {
1250 String tag = parser.getName();
1251 if ("p".equals(tag)) {
1252 // TODO: do we need to check that this package has the same signature
1253 // as before?
1254 String pkg = parser.getAttributeValue(null, "pkg");
1255 String cl = parser.getAttributeValue(null, "cl");
Romain Guyff3e61c62010-03-11 15:30:02 -08001256
1257 final PackageManager packageManager = mContext.getPackageManager();
1258 try {
1259 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1260 } catch (PackageManager.NameNotFoundException e) {
1261 String[] pkgs = packageManager.currentToCanonicalPackageNames(
1262 new String[] { pkg });
1263 pkg = pkgs[0];
1264 }
1265
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001266 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1267 if (p == null && mSafeMode) {
1268 // if we're in safe mode, make a temporary one
1269 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001270 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001271 p.info.provider = new ComponentName(pkg, cl);
1272 p.zombie = true;
1273 mInstalledProviders.add(p);
1274 }
1275 if (p != null) {
1276 // if it wasn't uninstalled or something
1277 loadedProviders.put(providerIndex, p);
1278 }
1279 providerIndex++;
1280 }
1281 else if ("h".equals(tag)) {
1282 Host host = new Host();
1283
1284 // TODO: do we need to check that this package has the same signature
1285 // as before?
1286 host.packageName = parser.getAttributeValue(null, "pkg");
1287 try {
1288 host.uid = getUidForPackage(host.packageName);
1289 } catch (PackageManager.NameNotFoundException ex) {
1290 host.zombie = true;
1291 }
1292 if (!host.zombie || mSafeMode) {
1293 // In safe mode, we don't discard the hosts we don't recognize
1294 // so that they're not pruned from our list. Otherwise, we do.
1295 host.hostId = Integer.parseInt(
1296 parser.getAttributeValue(null, "id"), 16);
1297 mHosts.add(host);
1298 }
1299 }
1300 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001301 AppWidgetId id = new AppWidgetId();
1302 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1303 if (id.appWidgetId >= mNextAppWidgetId) {
1304 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001305 }
1306
1307 String providerString = parser.getAttributeValue(null, "p");
1308 if (providerString != null) {
1309 // there's no provider if it hasn't been bound yet.
1310 // maybe we don't have to save this, but it brings the system
1311 // to the state it was in.
1312 int pIndex = Integer.parseInt(providerString, 16);
1313 id.provider = loadedProviders.get(pIndex);
1314 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001315 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001316 + pIndex + " which is " + id.provider);
1317 }
1318 if (id.provider == null) {
1319 // This provider is gone. We just let the host figure out
1320 // that this happened when it fails to load it.
1321 continue;
1322 }
1323 }
1324
1325 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1326 id.host = mHosts.get(hIndex);
1327 if (id.host == null) {
1328 // This host is gone.
1329 continue;
1330 }
1331
1332 if (id.provider != null) {
1333 id.provider.instances.add(id);
1334 }
1335 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001336 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001337 }
1338 }
1339 } while (type != XmlPullParser.END_DOCUMENT);
1340 success = true;
1341 } catch (NullPointerException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001342 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001343 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001344 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001345 } catch (XmlPullParserException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001346 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001347 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001348 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001349 } catch (IndexOutOfBoundsException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001350 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001351 }
1352 try {
1353 if (stream != null) {
1354 stream.close();
1355 }
1356 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001357 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001358 }
1359
1360 if (success) {
1361 // delete any hosts that didn't manage to get connected (should happen)
1362 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001363 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001364 pruneHostLocked(mHosts.get(i));
1365 }
1366 } else {
1367 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001368 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001369 mHosts.clear();
1370 final int N = mInstalledProviders.size();
1371 for (int i=0; i<N; i++) {
1372 mInstalledProviders.get(i).instances.clear();
1373 }
1374 }
1375 }
1376
1377 File savedStateTempFile() {
1378 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1379 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1380 }
1381
1382 File savedStateRealFile() {
1383 return new File("/data/system/" + SETTINGS_FILENAME);
1384 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1385 }
1386
1387 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1388 public void onReceive(Context context, Intent intent) {
1389 String action = intent.getAction();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001390 //Slog.d(TAG, "received " + action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001391 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1392 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001393 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1394 Locale revised = Locale.getDefault();
1395 if (revised == null || mLocale == null ||
1396 !(revised.equals(mLocale))) {
1397 mLocale = revised;
1398
1399 synchronized (mAppWidgetIds) {
1400 int N = mInstalledProviders.size();
1401 for (int i=N-1; i>=0; i--) {
1402 Provider p = mInstalledProviders.get(i);
1403 String pkgName = p.info.provider.getPackageName();
1404 updateProvidersForPackageLocked(pkgName);
1405 }
1406 saveStateLocked();
1407 }
1408 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001409 } else {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001410 boolean added = false;
Joe Onoratod070e892011-01-07 20:50:37 -08001411 boolean changed = false;
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001412 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001413 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001414 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1415 added = true;
Joe Onorato94258cd2010-08-24 16:45:40 -04001416 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001417 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1418 added = false;
1419 } else {
1420 Uri uri = intent.getData();
1421 if (uri == null) {
1422 return;
1423 }
1424 String pkgName = uri.getSchemeSpecificPart();
1425 if (pkgName == null) {
1426 return;
1427 }
1428 pkgList = new String[] { pkgName };
1429 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
Joe Onoratod070e892011-01-07 20:50:37 -08001430 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001431 }
1432 if (pkgList == null || pkgList.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001433 return;
1434 }
Joe Onoratod070e892011-01-07 20:50:37 -08001435 if (added || changed) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001436 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001437 Bundle extras = intent.getExtras();
Joe Onoratod070e892011-01-07 20:50:37 -08001438 if (changed || (extras != null &&
1439 extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001440 for (String pkgName : pkgList) {
1441 // The package was just upgraded
1442 updateProvidersForPackageLocked(pkgName);
1443 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001444 } else {
1445 // The package was just added
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001446 for (String pkgName : pkgList) {
1447 addProvidersForPackageLocked(pkgName);
1448 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 }
1450 saveStateLocked();
1451 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001452 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 Bundle extras = intent.getExtras();
1454 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1455 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1456 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001457 synchronized (mAppWidgetIds) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001458 for (String pkgName : pkgList) {
1459 removeProvidersForPackageLocked(pkgName);
1460 saveStateLocked();
1461 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001462 }
1463 }
1464 }
1465 }
1466 }
1467 };
1468
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001469 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001470 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001471 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001472 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1473 PackageManager.GET_META_DATA);
1474
Bjorn Bringert5f857802010-02-10 23:09:48 +00001475 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001476 for (int i=0; i<N; i++) {
1477 ResolveInfo ri = broadcastReceivers.get(i);
1478 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001479 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1480 continue;
1481 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001482 if (pkgName.equals(ai.packageName)) {
1483 addProviderLocked(ri);
1484 }
1485 }
1486 }
1487
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001488 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001489 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001490 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001491 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001492 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1493 PackageManager.GET_META_DATA);
1494
1495 // add the missing ones and collect which ones to keep
Bjorn Bringert5f857802010-02-10 23:09:48 +00001496 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001497 for (int i=0; i<N; i++) {
1498 ResolveInfo ri = broadcastReceivers.get(i);
1499 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001500 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1501 continue;
1502 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001503 if (pkgName.equals(ai.packageName)) {
1504 ComponentName component = new ComponentName(ai.packageName, ai.name);
1505 Provider p = lookupProviderLocked(component);
1506 if (p == null) {
1507 if (addProviderLocked(ri)) {
1508 keep.add(ai.name);
1509 }
1510 } else {
1511 Provider parsed = parseProviderInfoXml(component, ri);
1512 if (parsed != null) {
1513 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001514 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001515 p.info = parsed.info;
1516 // If it's enabled
1517 final int M = p.instances.size();
1518 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001519 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001520 // Reschedule for the new updatePeriodMillis (don't worry about handling
1521 // it specially if updatePeriodMillis didn't change because we just sent
1522 // an update, and the next one will be updatePeriodMillis from now).
1523 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001524 registerForBroadcastsLocked(p, appWidgetIds);
1525 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001526 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001527 AppWidgetId id = p.instances.get(j);
Joe Onoratoa8a8a422010-06-16 15:06:16 -04001528 id.views = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001529 if (id.host != null && id.host.callbacks != null) {
1530 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001531 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001532 } catch (RemoteException ex) {
1533 // It failed; remove the callback. No need to prune because
1534 // we know that this host is still referenced by this
1535 // instance.
1536 id.host.callbacks = null;
1537 }
1538 }
1539 }
1540 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001541 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001542 }
1543 }
1544 }
1545 }
1546 }
1547
1548 // prune the ones we don't want to keep
1549 N = mInstalledProviders.size();
1550 for (int i=N-1; i>=0; i--) {
1551 Provider p = mInstalledProviders.get(i);
1552 if (pkgName.equals(p.info.provider.getPackageName())
1553 && !keep.contains(p.info.provider.getClassName())) {
1554 removeProviderLocked(i, p);
1555 }
1556 }
1557 }
1558
1559 void removeProvidersForPackageLocked(String pkgName) {
1560 int N = mInstalledProviders.size();
1561 for (int i=N-1; i>=0; i--) {
1562 Provider p = mInstalledProviders.get(i);
1563 if (pkgName.equals(p.info.provider.getPackageName())) {
1564 removeProviderLocked(i, p);
1565 }
1566 }
1567
1568 // Delete the hosts for this package too
1569 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001570 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001571 // so we don't need to worry about sending DISABLE broadcasts to them.
1572 N = mHosts.size();
1573 for (int i=N-1; i>=0; i--) {
1574 Host host = mHosts.get(i);
1575 if (pkgName.equals(host.packageName)) {
1576 deleteHostLocked(host);
1577 }
1578 }
1579 }
1580}
1581