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