/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.PackageInfo; import android.content.pm.ResolveInfo; import android.content.pm.PackageItemInfo; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.gadget.GadgetManager; import android.gadget.GadgetInfo; import android.net.Uri; import android.os.Binder; import android.os.RemoteException; import android.os.SystemClock; import android.util.AttributeSet; import android.util.Config; import android.util.Log; import android.util.Xml; import android.widget.RemoteViews; import java.io.IOException; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.HashMap; import java.util.HashSet; import com.android.internal.gadget.IGadgetService; import com.android.internal.gadget.IGadgetHost; import com.android.internal.util.XmlUtils; import com.android.internal.util.FastXmlSerializer; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; class GadgetService extends IGadgetService.Stub { private static final String TAG = "GadgetService"; private static final boolean LOGD = Config.LOGD || false; private static final String SETTINGS_FILENAME = "gadgets.xml"; private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp"; /* * When identifying a Host or Provider based on the calling process, use the uid field. * When identifying a Host or Provider based on a package manager broadcast, use the * package given. */ static class Provider { int uid; GadgetInfo info; ArrayList instances = new ArrayList(); PendingIntent broadcast; int tag; // for use while saving state (the index) } static class Host { int uid; int hostId; String packageName; ArrayList instances = new ArrayList(); IGadgetHost callbacks; int tag; // for use while saving state (the index) } static class GadgetId { int gadgetId; Provider provider; RemoteViews views; Host host; } Context mContext; PackageManager mPackageManager; AlarmManager mAlarmManager; ArrayList mInstalledProviders = new ArrayList(); int mNextGadgetId = 1; ArrayList mGadgetIds = new ArrayList(); ArrayList mHosts = new ArrayList(); GadgetService(Context context) { mContext = context; mPackageManager = context.getPackageManager(); mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); getGadgetList(); loadStateLocked(); // Register for the boot completed broadcast, so we can send the // ENABLE broacasts. If we try to send them now, they time out, // because the system isn't ready to handle them yet. mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); // Register for broadcasts about package install, etc., so we can // update the provider list. IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); mContext.registerReceiver(mBroadcastReceiver, filter); } @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump from from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); return; } synchronized (mGadgetIds) { int N = mInstalledProviders.size(); pw.println("Providers: (size=" + N + ")"); for (int i=0; i getInstalledProviders() { synchronized (mGadgetIds) { final int N = mInstalledProviders.size(); ArrayList result = new ArrayList(N); for (int i=0; i instances = p.instances; final int N = instances.size(); for (int i=0; i updatedViews) { int callingUid = enforceCallingUid(packageName); synchronized (mGadgetIds) { Host host = lookupOrAddHostLocked(callingUid, packageName, hostId); host.callbacks = callbacks; updatedViews.clear(); ArrayList instances = host.instances; int N = instances.size(); int[] updatedIds = new int[N]; for (int i=0; i broadcastReceivers = pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); final int N = broadcastReceivers.size(); for (int i=0; i 0) { // if this is the first instance, set the alarm. otherwise, // rely on the fact that we've already set it and that // PendingIntent.getBroadcast will update the extras. boolean alreadyRegistered = p.broadcast != null; int instancesSize = p.instances.size(); Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, gadgetIds); intent.setComponent(p.info.provider); long token = Binder.clearCallingIdentity(); try { p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT); } finally { Binder.restoreCallingIdentity(token); } if (!alreadyRegistered) { mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + p.info.updatePeriodMillis, p.info.updatePeriodMillis, p.broadcast); } } } static int[] getGadgetIds(Provider p) { int instancesSize = p.instances.size(); int gadgetIds[] = new int[instancesSize]; for (int i=0; i 0) { out.startTag(null, "p"); out.attribute(null, "pkg", p.info.provider.getPackageName()); out.attribute(null, "cl", p.info.provider.getClassName()); out.endTag(null, "h"); p.tag = providerIndex; providerIndex++; } } N = mHosts.size(); for (int i=0; i loadedProviders = new HashMap(); do { type = parser.next(); if (type == XmlPullParser.START_TAG) { String tag = parser.getName(); if ("p".equals(tag)) { // TODO: do we need to check that this package has the same signature // as before? String pkg = parser.getAttributeValue(null, "pkg"); String cl = parser.getAttributeValue(null, "cl"); Provider p = lookupProviderLocked(new ComponentName(pkg, cl)); // if it wasn't uninstalled or something if (p != null) { loadedProviders.put(providerIndex, p); } providerIndex++; } else if ("h".equals(tag)) { Host host = new Host(); // TODO: do we need to check that this package has the same signature // as before? host.packageName = parser.getAttributeValue(null, "pkg"); try { host.uid = getUidForPackage(host.packageName); host.hostId = Integer.parseInt( parser.getAttributeValue(null, "id"), 16); mHosts.add(host); } catch (PackageManager.NameNotFoundException ex) { // Just ignore drop this entry, as if it has been uninstalled. // We need to deal with this case because of safe mode, but there's // a bug filed about it. } } else if ("g".equals(tag)) { GadgetId id = new GadgetId(); id.gadgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16); if (id.gadgetId >= mNextGadgetId) { mNextGadgetId = id.gadgetId + 1; } String providerString = parser.getAttributeValue(null, "p"); if (providerString != null) { // there's no provider if it hasn't been bound yet. // maybe we don't have to save this, but it brings the system // to the state it was in. int pIndex = Integer.parseInt(providerString, 16); id.provider = loadedProviders.get(pIndex); if (false) { Log.d(TAG, "bound gadgetId=" + id.gadgetId + " to provider " + pIndex + " which is " + id.provider); } if (id.provider == null) { // This provider is gone. We just let the host figure out // that this happened when it fails to load it. continue; } } int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16); id.host = mHosts.get(hIndex); if (id.host == null) { // This host is gone. continue; } if (id.provider != null) { id.provider.instances.add(id); } id.host.instances.add(id); mGadgetIds.add(id); } } } while (type != XmlPullParser.END_DOCUMENT); success = true; } catch (NullPointerException e) { Log.w(TAG, "failed parsing " + file, e); } catch (NumberFormatException e) { Log.w(TAG, "failed parsing " + file, e); } catch (XmlPullParserException e) { Log.w(TAG, "failed parsing " + file, e); } catch (IOException e) { Log.w(TAG, "failed parsing " + file, e); } catch (IndexOutOfBoundsException e) { Log.w(TAG, "failed parsing " + file, e); } try { if (stream != null) { stream.close(); } } catch (IOException e) { } if (success) { // delete any hosts that didn't manage to get connected (should happen) // if it matters, they'll be reconnected. final int N = mHosts.size(); for (int i=0; i broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); final int N = broadcastReceivers.size(); for (int i=0; i keep = new HashSet(); Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); List broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); // add the missing ones and collect which ones to keep int N = broadcastReceivers.size(); for (int i=0; i