OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / services / java / com / android / server / AppWidgetService.java
1 /*
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
17 package com.android.server;
18
19 import android.app.AlarmManager;
20 import android.app.PendingIntent;
21 import android.appwidget.AppWidgetManager;
22 import android.appwidget.AppWidgetProviderInfo;
23 import android.content.BroadcastReceiver;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.pm.ActivityInfo;
29 import android.content.pm.ApplicationInfo;
30 import android.content.pm.PackageManager;
31 import android.content.pm.PackageInfo;
32 import android.content.pm.ResolveInfo;
33 import android.content.res.Resources;
34 import android.content.res.TypedArray;
35 import android.content.res.XmlResourceParser;
36 import android.net.Uri;
37 import android.os.Binder;
38 import android.os.Bundle;
39 import android.os.Process;
40 import android.os.RemoteException;
41 import android.os.SystemClock;
42 import android.util.AttributeSet;
43 import android.util.Slog;
44 import android.util.TypedValue;
45 import android.util.Xml;
46 import android.widget.RemoteViews;
47
48 import java.io.IOException;
49 import java.io.File;
50 import java.io.FileDescriptor;
51 import java.io.FileInputStream;
52 import java.io.FileOutputStream;
53 import java.io.PrintWriter;
54 import java.util.ArrayList;
55 import java.util.List;
56 import java.util.Locale;
57 import java.util.HashMap;
58 import java.util.HashSet;
59
60 import com.android.internal.appwidget.IAppWidgetService;
61 import com.android.internal.appwidget.IAppWidgetHost;
62 import com.android.internal.util.FastXmlSerializer;
63
64 import org.xmlpull.v1.XmlPullParser;
65 import org.xmlpull.v1.XmlPullParserException;
66 import org.xmlpull.v1.XmlSerializer;
67
68 class AppWidgetService extends IAppWidgetService.Stub
69 {
70     private static final String TAG = "AppWidgetService";
71
72     private static final String SETTINGS_FILENAME = "appwidgets.xml";
73     private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
74     private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
75
76     /*
77      * When identifying a Host or Provider based on the calling process, use the uid field.
78      * When identifying a Host or Provider based on a package manager broadcast, use the
79      * package given.
80      */
81
82     static class Provider {
83         int uid;
84         AppWidgetProviderInfo info;
85         ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
86         PendingIntent broadcast;
87         boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
88         
89         int tag;    // for use while saving state (the index)
90     }
91
92     static class Host {
93         int uid;
94         int hostId;
95         String packageName;
96         ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
97         IAppWidgetHost callbacks;
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 AppWidgetId {
104         int appWidgetId;
105         Provider provider;
106         RemoteViews views;
107         Host host;
108     }
109
110     Context mContext;
111     Locale mLocale;
112     PackageManager mPackageManager;
113     AlarmManager mAlarmManager;
114     ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
115     int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
116     final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
117     ArrayList<Host> mHosts = new ArrayList<Host>();
118     boolean mSafeMode;
119
120     AppWidgetService(Context context) {
121         mContext = context;
122         mPackageManager = context.getPackageManager();
123         mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
124     }
125
126     public void systemReady(boolean safeMode) {
127         mSafeMode = safeMode;
128
129         loadAppWidgetList();
130         loadStateLocked();
131
132         // Register for the boot completed broadcast, so we can send the
133         // ENABLE broacasts.  If we try to send them now, they time out,
134         // because the system isn't ready to handle them yet.
135         mContext.registerReceiver(mBroadcastReceiver,
136                 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
137
138         // Register for configuration changes so we can update the names
139         // of the widgets when the locale changes.
140         mContext.registerReceiver(mBroadcastReceiver,
141                 new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
142
143         // Register for broadcasts about package install, etc., so we can
144         // update the provider list.
145         IntentFilter filter = new IntentFilter();
146         filter.addAction(Intent.ACTION_PACKAGE_ADDED);
147         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
148         filter.addDataScheme("package");
149         mContext.registerReceiver(mBroadcastReceiver, filter);
150         // Register for events related to sdcard installation.
151         IntentFilter sdFilter = new IntentFilter();
152         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
153         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
154         mContext.registerReceiver(mBroadcastReceiver, sdFilter);
155     }
156
157     @Override
158     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
159         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
160                 != PackageManager.PERMISSION_GRANTED) {
161             pw.println("Permission Denial: can't dump from from pid="
162                     + Binder.getCallingPid()
163                     + ", uid=" + Binder.getCallingUid());
164             return;
165         }
166
167         synchronized (mAppWidgetIds) {
168             int N = mInstalledProviders.size();
169             pw.println("Providers:");
170             for (int i=0; i<N; i++) {
171                 Provider p = mInstalledProviders.get(i);
172                 AppWidgetProviderInfo info = p.info;
173                 pw.print("  ["); pw.print(i); pw.print("] provider ");
174                         pw.print(info.provider.flattenToShortString());
175                         pw.println(':');
176                 pw.print("    min=("); pw.print(info.minWidth);
177                         pw.print("x"); pw.print(info.minHeight);
178                         pw.print(") updatePeriodMillis=");
179                         pw.print(info.updatePeriodMillis);
180                         pw.print(" initialLayout=#");
181                         pw.print(Integer.toHexString(info.initialLayout));
182                         pw.print(" zombie="); pw.println(p.zombie);
183             }
184
185             N = mAppWidgetIds.size();
186             pw.println(" ");
187             pw.println("AppWidgetIds:");
188             for (int i=0; i<N; i++) {
189                 AppWidgetId id = mAppWidgetIds.get(i);
190                 pw.print("  ["); pw.print(i); pw.print("] id=");
191                         pw.println(id.appWidgetId);
192                 pw.print("    hostId=");
193                         pw.print(id.host.hostId); pw.print(' ');
194                         pw.print(id.host.packageName); pw.print('/');
195                         pw.println(id.host.uid);
196                 if (id.provider != null) {
197                     pw.print("    provider=");
198                             pw.println(id.provider.info.provider.flattenToShortString());
199                 }
200                 if (id.host != null) {
201                     pw.print("    host.callbacks="); pw.println(id.host.callbacks);
202                 }
203                 if (id.views != null) {
204                     pw.print("    views="); pw.println(id.views);
205                 }
206             }
207
208             N = mHosts.size();
209             pw.println(" ");
210             pw.println("Hosts:");
211             for (int i=0; i<N; i++) {
212                 Host host = mHosts.get(i);
213                 pw.print("  ["); pw.print(i); pw.print("] hostId=");
214                         pw.print(host.hostId); pw.print(' ');
215                         pw.print(host.packageName); pw.print('/');
216                         pw.print(host.uid); pw.println(':');
217                 pw.print("    callbacks="); pw.println(host.callbacks);
218                 pw.print("    instances.size="); pw.print(host.instances.size());
219                         pw.print(" zombie="); pw.println(host.zombie);
220             }
221         }
222     }
223
224     public int allocateAppWidgetId(String packageName, int hostId) {
225         int callingUid = enforceCallingUid(packageName);
226         synchronized (mAppWidgetIds) {
227             int appWidgetId = mNextAppWidgetId++;
228
229             Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
230
231             AppWidgetId id = new AppWidgetId();
232             id.appWidgetId = appWidgetId;
233             id.host = host;
234
235             host.instances.add(id);
236             mAppWidgetIds.add(id);
237
238             saveStateLocked();
239
240             return appWidgetId;
241         }
242     }
243
244     public void deleteAppWidgetId(int appWidgetId) {
245         synchronized (mAppWidgetIds) {
246             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
247             if (id != null) {
248                 deleteAppWidgetLocked(id);
249                 saveStateLocked();
250             }
251         }
252     }
253
254     public void deleteHost(int hostId) {
255         synchronized (mAppWidgetIds) {
256             int callingUid = getCallingUid();
257             Host host = lookupHostLocked(callingUid, hostId);
258             if (host != null) {
259                 deleteHostLocked(host);
260                 saveStateLocked();
261             }
262         }
263     }
264
265     public void deleteAllHosts() {
266         synchronized (mAppWidgetIds) {
267             int callingUid = getCallingUid();
268             final int N = mHosts.size();
269             boolean changed = false;
270             for (int i=N-1; i>=0; i--) {
271                 Host host = mHosts.get(i);
272                 if (host.uid == callingUid) {
273                     deleteHostLocked(host);
274                     changed = true;
275                 }
276             }
277             if (changed) {
278                 saveStateLocked();
279             }
280         }
281     }
282
283     void deleteHostLocked(Host host) {
284         final int N = host.instances.size();
285         for (int i=N-1; i>=0; i--) {
286             AppWidgetId id = host.instances.get(i);
287             deleteAppWidgetLocked(id);
288         }
289         host.instances.clear();
290         mHosts.remove(host);
291         // it's gone or going away, abruptly drop the callback connection
292         host.callbacks = null;
293     }
294
295     void deleteAppWidgetLocked(AppWidgetId id) {
296         Host host = id.host;
297         host.instances.remove(id);
298         pruneHostLocked(host);
299
300         mAppWidgetIds.remove(id);
301
302         Provider p = id.provider;
303         if (p != null) {
304             p.instances.remove(id);
305             if (!p.zombie) {
306                 // send the broacast saying that this appWidgetId has been deleted
307                 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
308                 intent.setComponent(p.info.provider);
309                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
310                 mContext.sendBroadcast(intent);
311                 if (p.instances.size() == 0) {
312                     // cancel the future updates
313                     cancelBroadcasts(p);
314
315                     // send the broacast saying that the provider is not in use any more
316                     intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
317                     intent.setComponent(p.info.provider);
318                     mContext.sendBroadcast(intent);
319                 }
320             }
321         }
322     }
323
324     void cancelBroadcasts(Provider p) {
325         if (p.broadcast != null) {
326             mAlarmManager.cancel(p.broadcast);
327             long token = Binder.clearCallingIdentity();
328             try {
329                 p.broadcast.cancel();
330             } finally {
331                 Binder.restoreCallingIdentity(token);
332             }
333             p.broadcast = null;
334         }
335     }
336
337     public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
338         mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
339                 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
340         synchronized (mAppWidgetIds) {
341             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
342             if (id == null) {
343                 throw new IllegalArgumentException("bad appWidgetId");
344             }
345             if (id.provider != null) {
346                 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
347                         + id.provider.info.provider);
348             }
349             Provider p = lookupProviderLocked(provider);
350             if (p == null) {
351                 throw new IllegalArgumentException("not a appwidget provider: " + provider);
352             }
353             if (p.zombie) {
354                 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
355                         + " safe mode: " + provider);
356             }
357
358             id.provider = p;
359             p.instances.add(id);
360             int instancesSize = p.instances.size();
361             if (instancesSize == 1) {
362                 // tell the provider that it's ready
363                 sendEnableIntentLocked(p);
364             }
365
366             // send an update now -- We need this update now, and just for this appWidgetId.
367             // It's less critical when the next one happens, so when we schdule the next one,
368             // we add updatePeriodMillis to its start time.  That time will have some slop,
369             // but that's okay.
370             sendUpdateIntentLocked(p, new int[] { appWidgetId });
371
372             // schedule the future updates
373             registerForBroadcastsLocked(p, getAppWidgetIds(p));
374             saveStateLocked();
375         }
376     }
377
378     public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
379         synchronized (mAppWidgetIds) {
380             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
381             if (id != null && id.provider != null && !id.provider.zombie) {
382                 return id.provider.info;
383             }
384             return null;
385         }
386     }
387
388     public RemoteViews getAppWidgetViews(int appWidgetId) {
389         synchronized (mAppWidgetIds) {
390             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
391             if (id != null) {
392                 return id.views;
393             }
394             return null;
395         }
396     }
397
398     public List<AppWidgetProviderInfo> getInstalledProviders() {
399         synchronized (mAppWidgetIds) {
400             final int N = mInstalledProviders.size();
401             ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
402             for (int i=0; i<N; i++) {
403                 Provider p = mInstalledProviders.get(i);
404                 if (!p.zombie) {
405                     result.add(p.info);
406                 }
407             }
408             return result;
409         }
410     }
411
412     public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
413         if (appWidgetIds == null) {
414             return;
415         }
416         if (appWidgetIds.length == 0) {
417             return;
418         }
419         final int N = appWidgetIds.length;
420
421         synchronized (mAppWidgetIds) {
422             for (int i=0; i<N; i++) {
423                 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
424                 updateAppWidgetInstanceLocked(id, views);
425             }
426         }
427     }
428
429     public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
430         synchronized (mAppWidgetIds) {
431             Provider p = lookupProviderLocked(provider);
432             if (p == null) {
433                 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
434                 return;
435             }
436             ArrayList<AppWidgetId> instances = p.instances;
437             final int N = instances.size();
438             for (int i=0; i<N; i++) {
439                 AppWidgetId id = instances.get(i);
440                 updateAppWidgetInstanceLocked(id, views);
441             }
442         }
443     }
444
445     void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
446         // allow for stale appWidgetIds and other badness
447         // lookup also checks that the calling process can access the appWidgetId
448         // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
449         if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
450             id.views = views;
451
452             // is anyone listening?
453             if (id.host.callbacks != null) {
454                 try {
455                     // the lock is held, but this is a oneway call
456                     id.host.callbacks.updateAppWidget(id.appWidgetId, views);
457                 } catch (RemoteException e) {
458                     // It failed; remove the callback. No need to prune because
459                     // we know that this host is still referenced by this instance.
460                     id.host.callbacks = null;
461                 }
462             }
463         }
464     }
465
466     public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
467             List<RemoteViews> updatedViews) {
468         int callingUid = enforceCallingUid(packageName);
469         synchronized (mAppWidgetIds) {
470             Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
471             host.callbacks = callbacks;
472
473             updatedViews.clear();
474
475             ArrayList<AppWidgetId> instances = host.instances;
476             int N = instances.size();
477             int[] updatedIds = new int[N];
478             for (int i=0; i<N; i++) {
479                 AppWidgetId id = instances.get(i);
480                 updatedIds[i] = id.appWidgetId;
481                 updatedViews.add(id.views);
482             }
483             return updatedIds;
484         }
485     }
486
487     public void stopListening(int hostId) {
488         synchronized (mAppWidgetIds) {
489             Host host = lookupHostLocked(getCallingUid(), hostId);
490             if (host != null) {
491                 host.callbacks = null;
492                 pruneHostLocked(host);
493             }
494         }
495     }
496
497     boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
498         if (id.host.uid == callingUid) {
499             // Apps hosting the AppWidget have access to it.
500             return true;
501         }
502         if (id.provider != null && id.provider.uid == callingUid) {
503             // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
504             return true;
505         }
506         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
507                 == PackageManager.PERMISSION_GRANTED) {
508             // Apps that can bind have access to all appWidgetIds.
509             return true;
510         }
511         // Nobody else can access it.
512         return false;
513     }
514
515    AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
516         int callingUid = getCallingUid();
517         final int N = mAppWidgetIds.size();
518         for (int i=0; i<N; i++) {
519             AppWidgetId id = mAppWidgetIds.get(i);
520             if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
521                 return id;
522             }
523         }
524         return null;
525     }
526
527     Provider lookupProviderLocked(ComponentName provider) {
528         final String className = provider.getClassName();
529         final int N = mInstalledProviders.size();
530         for (int i=0; i<N; i++) {
531             Provider p = mInstalledProviders.get(i);
532             if (p.info.provider.equals(provider) || className.equals(p.info.oldName)) {
533                 return p;
534             }
535         }
536         return null;
537     }
538
539     Host lookupHostLocked(int uid, int hostId) {
540         final int N = mHosts.size();
541         for (int i=0; i<N; i++) {
542             Host h = mHosts.get(i);
543             if (h.uid == uid && h.hostId == hostId) {
544                 return h;
545             }
546         }
547         return null;
548     }
549
550     Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
551         final int N = mHosts.size();
552         for (int i=0; i<N; i++) {
553             Host h = mHosts.get(i);
554             if (h.hostId == hostId && h.packageName.equals(packageName)) {
555                 return h;
556             }
557         }
558         Host host = new Host();
559         host.packageName = packageName;
560         host.uid = uid;
561         host.hostId = hostId;
562         mHosts.add(host);
563         return host;
564     }
565
566     void pruneHostLocked(Host host) {
567         if (host.instances.size() == 0 && host.callbacks == null) {
568             mHosts.remove(host);
569         }
570     }
571
572     void loadAppWidgetList() {
573         PackageManager pm = mPackageManager;
574
575         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
576         List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
577                 PackageManager.GET_META_DATA);
578
579         final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
580         for (int i=0; i<N; i++) {
581             ResolveInfo ri = broadcastReceivers.get(i);
582             addProviderLocked(ri);
583         }
584     }
585
586     boolean addProviderLocked(ResolveInfo ri) {
587         Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
588                     ri.activityInfo.name), ri);
589         if (p != null) {
590             mInstalledProviders.add(p);
591             return true;
592         } else {
593             return false;
594         }
595     }
596
597     void removeProviderLocked(int index, Provider p) {
598         int N = p.instances.size();
599         for (int i=0; i<N; i++) {
600             AppWidgetId id = p.instances.get(i);
601             // Call back with empty RemoteViews
602             updateAppWidgetInstanceLocked(id, null);
603             // Stop telling the host about updates for this from now on
604             cancelBroadcasts(p);
605             // clear out references to this appWidgetId
606             id.host.instances.remove(id);
607             mAppWidgetIds.remove(id);
608             id.provider = null;
609             pruneHostLocked(id.host);
610             id.host = null;
611         }
612         p.instances.clear();
613         mInstalledProviders.remove(index);
614         // no need to send the DISABLE broadcast, since the receiver is gone anyway
615         cancelBroadcasts(p);
616     }
617
618     void sendEnableIntentLocked(Provider p) {
619         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
620         intent.setComponent(p.info.provider);
621         mContext.sendBroadcast(intent);
622     }
623
624     void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
625         if (appWidgetIds != null && appWidgetIds.length > 0) {
626             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
627             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
628             intent.setComponent(p.info.provider);
629             mContext.sendBroadcast(intent);
630         }
631     }
632
633     void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
634         if (p.info.updatePeriodMillis > 0) {
635             // if this is the first instance, set the alarm.  otherwise,
636             // rely on the fact that we've already set it and that
637             // PendingIntent.getBroadcast will update the extras.
638             boolean alreadyRegistered = p.broadcast != null;
639             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
640             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
641             intent.setComponent(p.info.provider);
642             long token = Binder.clearCallingIdentity();
643             try {
644                 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
645                         PendingIntent.FLAG_UPDATE_CURRENT);
646             } finally {
647                 Binder.restoreCallingIdentity(token);
648             }
649             if (!alreadyRegistered) {
650                 long period = p.info.updatePeriodMillis;
651                 if (period < MIN_UPDATE_PERIOD) {
652                     period = MIN_UPDATE_PERIOD;
653                 }
654                 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
655                         SystemClock.elapsedRealtime() + period, period, p.broadcast);
656             }
657         }
658     }
659     
660     static int[] getAppWidgetIds(Provider p) {
661         int instancesSize = p.instances.size();
662         int appWidgetIds[] = new int[instancesSize];
663         for (int i=0; i<instancesSize; i++) {
664             appWidgetIds[i] = p.instances.get(i).appWidgetId;
665         }
666         return appWidgetIds;
667     }
668     
669     public int[] getAppWidgetIds(ComponentName provider) {
670         synchronized (mAppWidgetIds) {
671             Provider p = lookupProviderLocked(provider);
672             if (p != null && getCallingUid() == p.uid) {
673                 return getAppWidgetIds(p);                
674             } else {
675                 return new int[0];
676             }
677         }
678     }
679
680     private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
681         Provider p = null;
682
683         ActivityInfo activityInfo = ri.activityInfo;
684         XmlResourceParser parser = null;
685         try {
686             parser = activityInfo.loadXmlMetaData(mPackageManager,
687                     AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
688             if (parser == null) {
689                 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
690                         + "AppWidget provider '" + component + '\'');
691                 return null;
692             }
693         
694             AttributeSet attrs = Xml.asAttributeSet(parser);
695             
696             int type;
697             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
698                     && type != XmlPullParser.START_TAG) {
699                 // drain whitespace, comments, etc.
700             }
701             
702             String nodeName = parser.getName();
703             if (!"appwidget-provider".equals(nodeName)) {
704                 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
705                         + " AppWidget provider '" + component + '\'');
706                 return null;
707             }
708
709             p = new Provider();
710             AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
711             // If metaData was null, we would have returned earlier when getting
712             // the parser No need to do the check here
713             info.oldName = activityInfo.metaData.getString(
714                     AppWidgetManager.META_DATA_APPWIDGET_OLD_NAME);
715
716             info.provider = component;
717             p.uid = activityInfo.applicationInfo.uid;
718
719             Resources res = mPackageManager.getResourcesForApplication(
720                     activityInfo.applicationInfo);
721             
722             TypedArray sa = res.obtainAttributes(attrs,
723                     com.android.internal.R.styleable.AppWidgetProviderInfo);
724             
725             // These dimensions has to be resolved in the application's context.
726             // We simply send back the raw complex data, which will be
727             // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
728             TypedValue value = sa.peekValue(
729                     com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
730             info.minWidth = value != null ? value.data : 0; 
731             value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
732             info.minHeight = value != null ? value.data : 0;
733                     
734             info.updatePeriodMillis = sa.getInt(
735                     com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
736             info.initialLayout = sa.getResourceId(
737                     com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
738             String className = sa.getString(
739                     com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
740             if (className != null) {
741                 info.configure = new ComponentName(component.getPackageName(), className);
742             }
743             info.label = activityInfo.loadLabel(mPackageManager).toString();
744             info.icon = ri.getIconResource();
745             sa.recycle();
746         } catch (Exception e) {
747             // Ok to catch Exception here, because anything going wrong because
748             // of what a client process passes to us should not be fatal for the
749             // system process.
750             Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
751             return null;
752         } finally {
753             if (parser != null) parser.close();
754         }
755         return p;
756     }
757
758     int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
759         PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
760         if (pkgInfo == null || pkgInfo.applicationInfo == null) {
761             throw new PackageManager.NameNotFoundException();
762         }
763         return pkgInfo.applicationInfo.uid;
764     }
765
766     int enforceCallingUid(String packageName) throws IllegalArgumentException {
767         int callingUid = getCallingUid();
768         int packageUid;
769         try {
770             packageUid = getUidForPackage(packageName);
771         } catch (PackageManager.NameNotFoundException ex) {
772             throw new IllegalArgumentException("packageName and uid don't match packageName="
773                     + packageName);
774         }
775         if (callingUid != packageUid && Process.supportsProcesses()) {
776             throw new IllegalArgumentException("packageName and uid don't match packageName="
777                     + packageName);
778         }
779         return callingUid;
780     }
781
782     void sendInitialBroadcasts() {
783         synchronized (mAppWidgetIds) {
784             final int N = mInstalledProviders.size();
785             for (int i=0; i<N; i++) {
786                 Provider p = mInstalledProviders.get(i);
787                 if (p.instances.size() > 0) {
788                     sendEnableIntentLocked(p);
789                     int[] appWidgetIds = getAppWidgetIds(p);
790                     sendUpdateIntentLocked(p, appWidgetIds);
791                     registerForBroadcastsLocked(p, appWidgetIds);
792                 }
793             }
794         }
795     }
796
797     // only call from initialization -- it assumes that the data structures are all empty
798     void loadStateLocked() {
799         File temp = savedStateTempFile();
800         File real = savedStateRealFile();
801
802         // prefer the real file.  If it doesn't exist, use the temp one, and then copy it to the
803         // real one.  if there is both a real file and a temp one, assume that the temp one isn't
804         // fully written and delete it.
805         if (real.exists()) {
806             readStateFromFileLocked(real);
807             if (temp.exists()) {
808                 //noinspection ResultOfMethodCallIgnored
809                 temp.delete();
810             }
811         } else if (temp.exists()) {
812             readStateFromFileLocked(temp);
813             //noinspection ResultOfMethodCallIgnored
814             temp.renameTo(real);
815         }
816     }
817     
818     void saveStateLocked() {
819         File temp = savedStateTempFile();
820         File real = savedStateRealFile();
821
822         if (!real.exists()) {
823             // If the real one doesn't exist, it's either because this is the first time
824             // or because something went wrong while copying them.  In this case, we can't
825             // trust anything that's in temp.  In order to have the loadState code not
826             // use the temporary one until it's fully written, create an empty file
827             // for real, which will we'll shortly delete.
828             try {
829                 //noinspection ResultOfMethodCallIgnored
830                 real.createNewFile();
831             } catch (IOException e) {
832                 // Ignore
833             }
834         }
835
836         if (temp.exists()) {
837             //noinspection ResultOfMethodCallIgnored
838             temp.delete();
839         }
840
841         if (!writeStateToFileLocked(temp)) {
842             Slog.w(TAG, "Failed to persist new settings");
843             return;
844         }
845
846         //noinspection ResultOfMethodCallIgnored
847         real.delete();
848         //noinspection ResultOfMethodCallIgnored
849         temp.renameTo(real);
850     }
851
852     boolean writeStateToFileLocked(File file) {
853         FileOutputStream stream = null;
854         int N;
855
856         try {
857             stream = new FileOutputStream(file, false);
858             XmlSerializer out = new FastXmlSerializer();
859             out.setOutput(stream, "utf-8");
860             out.startDocument(null, true);
861
862             
863             out.startTag(null, "gs");
864
865             int providerIndex = 0;
866             N = mInstalledProviders.size();
867             for (int i=0; i<N; i++) {
868                 Provider p = mInstalledProviders.get(i);
869                 if (p.instances.size() > 0) {
870                     out.startTag(null, "p");
871                     out.attribute(null, "pkg", p.info.provider.getPackageName());
872                     out.attribute(null, "cl", p.info.provider.getClassName());
873                     out.endTag(null, "p");
874                     p.tag = providerIndex;
875                     providerIndex++;
876                 }
877             }
878
879             N = mHosts.size();
880             for (int i=0; i<N; i++) {
881                 Host host = mHosts.get(i);
882                 out.startTag(null, "h");
883                 out.attribute(null, "pkg", host.packageName);
884                 out.attribute(null, "id", Integer.toHexString(host.hostId));
885                 out.endTag(null, "h");
886                 host.tag = i;
887             }
888
889             N = mAppWidgetIds.size();
890             for (int i=0; i<N; i++) {
891                 AppWidgetId id = mAppWidgetIds.get(i);
892                 out.startTag(null, "g");
893                 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
894                 out.attribute(null, "h", Integer.toHexString(id.host.tag));
895                 if (id.provider != null) {
896                     out.attribute(null, "p", Integer.toHexString(id.provider.tag));
897                 }
898                 out.endTag(null, "g");
899             }
900
901             out.endTag(null, "gs");
902
903             out.endDocument();
904             stream.close();
905             return true;
906         } catch (IOException e) {
907             try {
908                 if (stream != null) {
909                     stream.close();
910                 }
911             } catch (IOException ex) {
912                 // Ignore
913             }
914             if (file.exists()) {
915                 //noinspection ResultOfMethodCallIgnored
916                 file.delete();
917             }
918             return false;
919         }
920     }
921
922     void readStateFromFileLocked(File file) {
923         FileInputStream stream = null;
924
925         boolean success = false;
926
927         try {
928             stream = new FileInputStream(file);
929             XmlPullParser parser = Xml.newPullParser();
930             parser.setInput(stream, null);
931
932             int type;
933             int providerIndex = 0;
934             HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
935             do {
936                 type = parser.next();
937                 if (type == XmlPullParser.START_TAG) {
938                     String tag = parser.getName();
939                     if ("p".equals(tag)) {
940                         // TODO: do we need to check that this package has the same signature
941                         // as before?
942                         String pkg = parser.getAttributeValue(null, "pkg");
943                         String cl = parser.getAttributeValue(null, "cl");
944
945                         final PackageManager packageManager = mContext.getPackageManager();
946                         try {
947                             packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
948                         } catch (PackageManager.NameNotFoundException e) {
949                             String[] pkgs = packageManager.currentToCanonicalPackageNames(
950                                     new String[] { pkg });
951                             pkg = pkgs[0];
952                         }
953
954                         Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
955                         if (p == null && mSafeMode) {
956                             // if we're in safe mode, make a temporary one
957                             p = new Provider();
958                             p.info = new AppWidgetProviderInfo();
959                             p.info.provider = new ComponentName(pkg, cl);
960                             p.zombie = true;
961                             mInstalledProviders.add(p);
962                         }
963                         if (p != null) {
964                             // if it wasn't uninstalled or something
965                             loadedProviders.put(providerIndex, p);
966                         }
967                         providerIndex++;
968                     }
969                     else if ("h".equals(tag)) {
970                         Host host = new Host();
971
972                         // TODO: do we need to check that this package has the same signature
973                         // as before?
974                         host.packageName = parser.getAttributeValue(null, "pkg");
975                         try {
976                             host.uid = getUidForPackage(host.packageName);
977                         } catch (PackageManager.NameNotFoundException ex) {
978                             host.zombie = true;
979                         }
980                         if (!host.zombie || mSafeMode) {
981                             // In safe mode, we don't discard the hosts we don't recognize
982                             // so that they're not pruned from our list.  Otherwise, we do.
983                             host.hostId = Integer.parseInt(
984                                     parser.getAttributeValue(null, "id"), 16);
985                             mHosts.add(host);
986                         }
987                     }
988                     else if ("g".equals(tag)) {
989                         AppWidgetId id = new AppWidgetId();
990                         id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
991                         if (id.appWidgetId >= mNextAppWidgetId) {
992                             mNextAppWidgetId = id.appWidgetId + 1;
993                         }
994
995                         String providerString = parser.getAttributeValue(null, "p");
996                         if (providerString != null) {
997                             // there's no provider if it hasn't been bound yet.
998                             // maybe we don't have to save this, but it brings the system
999                             // to the state it was in.
1000                             int pIndex = Integer.parseInt(providerString, 16);
1001                             id.provider = loadedProviders.get(pIndex);
1002                             if (false) {
1003                                 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1004                                         + pIndex + " which is " + id.provider);
1005                             }
1006                             if (id.provider == null) {
1007                                 // This provider is gone.  We just let the host figure out
1008                                 // that this happened when it fails to load it.
1009                                 continue;
1010                             }
1011                         }
1012
1013                         int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1014                         id.host = mHosts.get(hIndex);
1015                         if (id.host == null) {
1016                             // This host is gone.
1017                             continue;
1018                         }
1019
1020                         if (id.provider != null) {
1021                             id.provider.instances.add(id);
1022                         }
1023                         id.host.instances.add(id);
1024                         mAppWidgetIds.add(id);
1025                     }
1026                 }
1027             } while (type != XmlPullParser.END_DOCUMENT);
1028             success = true;
1029         } catch (NullPointerException e) {
1030             Slog.w(TAG, "failed parsing " + file, e);
1031         } catch (NumberFormatException e) {
1032             Slog.w(TAG, "failed parsing " + file, e);
1033         } catch (XmlPullParserException e) {
1034             Slog.w(TAG, "failed parsing " + file, e);
1035         } catch (IOException e) {
1036             Slog.w(TAG, "failed parsing " + file, e);
1037         } catch (IndexOutOfBoundsException e) {
1038             Slog.w(TAG, "failed parsing " + file, e);
1039         }
1040         try {
1041             if (stream != null) {
1042                 stream.close();
1043             }
1044         } catch (IOException e) {
1045             // Ignore
1046         }
1047
1048         if (success) {
1049             // delete any hosts that didn't manage to get connected (should happen)
1050             // if it matters, they'll be reconnected.
1051             for (int i=mHosts.size()-1; i>=0; i--) {
1052                 pruneHostLocked(mHosts.get(i));
1053             }
1054         } else {
1055             // failed reading, clean up
1056             mAppWidgetIds.clear();
1057             mHosts.clear();
1058             final int N = mInstalledProviders.size();
1059             for (int i=0; i<N; i++) {
1060                 mInstalledProviders.get(i).instances.clear();
1061             }
1062         }
1063     }
1064
1065     File savedStateTempFile() {
1066         return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1067         //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1068     }
1069
1070     File savedStateRealFile() {
1071         return new File("/data/system/" + SETTINGS_FILENAME);
1072         //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1073     }
1074
1075     BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1076         public void onReceive(Context context, Intent intent) {
1077             String action = intent.getAction();
1078             //Slog.d(TAG, "received " + action);
1079             if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1080                 sendInitialBroadcasts();
1081             } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1082                 Locale revised = Locale.getDefault();
1083                 if (revised == null || mLocale == null ||
1084                     !(revised.equals(mLocale))) {
1085                     mLocale = revised;
1086
1087                     synchronized (mAppWidgetIds) {
1088                         int N = mInstalledProviders.size();
1089                         for (int i=N-1; i>=0; i--) {
1090                             Provider p = mInstalledProviders.get(i);
1091                             String pkgName = p.info.provider.getPackageName();
1092                             updateProvidersForPackageLocked(pkgName);
1093                         }
1094                         saveStateLocked();
1095                     }
1096                 }
1097             } else {
1098                 boolean added = false;
1099                 String pkgList[] = null;
1100                 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
1101                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1102                     added = true;
1103                 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
1104                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1105                     added = false;
1106                 } else  {
1107                     Uri uri = intent.getData();
1108                     if (uri == null) {
1109                         return;
1110                     }
1111                     String pkgName = uri.getSchemeSpecificPart();
1112                     if (pkgName == null) {
1113                         return;
1114                     }
1115                     pkgList = new String[] { pkgName };
1116                     added = Intent.ACTION_PACKAGE_ADDED.equals(action);
1117                 }
1118                 if (pkgList == null || pkgList.length == 0) {
1119                     return;
1120                 }
1121                 if (added) {
1122                     synchronized (mAppWidgetIds) {
1123                         Bundle extras = intent.getExtras();
1124                         if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1125                             for (String pkgName : pkgList) {
1126                                 // The package was just upgraded
1127                                 updateProvidersForPackageLocked(pkgName);
1128                             }
1129                         } else {
1130                             // The package was just added
1131                             for (String pkgName : pkgList) {
1132                                 addProvidersForPackageLocked(pkgName);
1133                             }
1134                         }
1135                         saveStateLocked();
1136                     }
1137                 } else {
1138                     Bundle extras = intent.getExtras();
1139                     if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1140                         // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
1141                     } else {
1142                         synchronized (mAppWidgetIds) {
1143                             for (String pkgName : pkgList) {
1144                                 removeProvidersForPackageLocked(pkgName);
1145                                 saveStateLocked();
1146                             }
1147                         }
1148                     }
1149                 }
1150             }
1151         }
1152     };
1153
1154     void addProvidersForPackageLocked(String pkgName) {
1155         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1156         intent.setPackage(pkgName);
1157         List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1158                 PackageManager.GET_META_DATA);
1159
1160         final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1161         for (int i=0; i<N; i++) {
1162             ResolveInfo ri = broadcastReceivers.get(i);
1163             ActivityInfo ai = ri.activityInfo;
1164             if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1165                 continue;
1166             }
1167             if (pkgName.equals(ai.packageName)) {
1168                 addProviderLocked(ri);
1169             }
1170         }
1171     }
1172
1173     void updateProvidersForPackageLocked(String pkgName) {
1174         HashSet<String> keep = new HashSet<String>();
1175         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1176         intent.setPackage(pkgName);
1177         List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1178                 PackageManager.GET_META_DATA);
1179
1180         // add the missing ones and collect which ones to keep
1181         int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1182         for (int i=0; i<N; i++) {
1183             ResolveInfo ri = broadcastReceivers.get(i);
1184             ActivityInfo ai = ri.activityInfo;
1185             if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1186                 continue;
1187             }
1188             if (pkgName.equals(ai.packageName)) {
1189                 ComponentName component = new ComponentName(ai.packageName, ai.name);
1190                 Provider p = lookupProviderLocked(component);
1191                 if (p == null) {
1192                     if (addProviderLocked(ri)) {
1193                         keep.add(ai.name);
1194                     }
1195                 } else {
1196                     Provider parsed = parseProviderInfoXml(component, ri);
1197                     if (parsed != null) {
1198                         keep.add(ai.name);
1199                         // Use the new AppWidgetProviderInfo.
1200                         p.info = parsed.info;
1201                         // If it's enabled
1202                         final int M = p.instances.size();
1203                         if (M > 0) {
1204                             int[] appWidgetIds = getAppWidgetIds(p);
1205                             // Reschedule for the new updatePeriodMillis (don't worry about handling
1206                             // it specially if updatePeriodMillis didn't change because we just sent
1207                             // an update, and the next one will be updatePeriodMillis from now).
1208                             cancelBroadcasts(p);
1209                             registerForBroadcastsLocked(p, appWidgetIds);
1210                             // If it's currently showing, call back with the new AppWidgetProviderInfo.
1211                             for (int j=0; j<M; j++) {
1212                                 AppWidgetId id = p.instances.get(j);
1213                                 id.views = null;
1214                                 if (id.host != null && id.host.callbacks != null) {
1215                                     try {
1216                                         id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1217                                     } catch (RemoteException ex) {
1218                                         // It failed; remove the callback. No need to prune because
1219                                         // we know that this host is still referenced by this
1220                                         // instance.
1221                                         id.host.callbacks = null;
1222                                     }
1223                                 }
1224                             }
1225                             // Now that we've told the host, push out an update.
1226                             sendUpdateIntentLocked(p, appWidgetIds);
1227                         }
1228                     }
1229                 }
1230             }
1231         }
1232
1233         // prune the ones we don't want to keep
1234         N = mInstalledProviders.size();
1235         for (int i=N-1; i>=0; i--) {
1236             Provider p = mInstalledProviders.get(i);
1237             if (pkgName.equals(p.info.provider.getPackageName())
1238                     && !keep.contains(p.info.provider.getClassName())) {
1239                 removeProviderLocked(i, p);
1240             }
1241         }
1242     }
1243
1244     void removeProvidersForPackageLocked(String pkgName) {
1245         int N = mInstalledProviders.size();
1246         for (int i=N-1; i>=0; i--) {
1247             Provider p = mInstalledProviders.get(i);
1248             if (pkgName.equals(p.info.provider.getPackageName())) {
1249                 removeProviderLocked(i, p);
1250             }
1251         }
1252
1253         // Delete the hosts for this package too
1254         //
1255         // By now, we have removed any AppWidgets that were in any hosts here,
1256         // so we don't need to worry about sending DISABLE broadcasts to them.
1257         N = mHosts.size();
1258         for (int i=N-1; i>=0; i--) {
1259             Host host = mHosts.get(i);
1260             if (pkgName.equals(host.packageName)) {
1261                 deleteHostLocked(host);
1262             }
1263         }
1264     }
1265 }
1266