293 lines
10 KiB
Java
293 lines
10 KiB
Java
/*
|
|
* 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.content.ComponentName;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.pm.ActivityInfo;
|
|
import android.content.pm.PackageManager;
|
|
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.os.Binder;
|
|
import android.util.AttributeSet;
|
|
import android.util.Log;
|
|
import android.util.Xml;
|
|
|
|
import java.io.FileDescriptor;
|
|
import java.io.PrintWriter;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
import com.android.internal.gadget.IGadgetService;
|
|
|
|
import org.xmlpull.v1.XmlPullParser;
|
|
import org.xmlpull.v1.XmlPullParserException;
|
|
|
|
class GadgetService extends IGadgetService.Stub
|
|
{
|
|
private static final String TAG = "GadgetService";
|
|
|
|
static class GadgetId {
|
|
int gadgetId;
|
|
String hostPackage;
|
|
GadgetInfo info;
|
|
}
|
|
|
|
Context mContext;
|
|
PackageManager mPackageManager;
|
|
ArrayList<GadgetInfo> mInstalledProviders;
|
|
int mNextGadgetId = 1;
|
|
ArrayList<GadgetId> mGadgetIds = new ArrayList();
|
|
|
|
GadgetService(Context context) {
|
|
mContext = context;
|
|
mPackageManager = context.getPackageManager();
|
|
mInstalledProviders = getGadgetList();
|
|
}
|
|
|
|
@Override
|
|
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
|
if (mContext.checkCallingPermission(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<N; i++) {
|
|
GadgetInfo info = mInstalledProviders.get(i);
|
|
pw.println(" [" + i + "] provder=" + info.provider
|
|
+ " min=(" + info.minWidth + "x" + info.minHeight + ")"
|
|
+ " updatePeriodMillis=" + info.updatePeriodMillis
|
|
+ " initialLayout=" + info.initialLayout);
|
|
}
|
|
|
|
N = mGadgetIds.size();
|
|
pw.println("GadgetIds: (size=" + N + ")");
|
|
for (int i=0; i<N; i++) {
|
|
GadgetId id = mGadgetIds.get(i);
|
|
pw.println(" [" + i + "] gadgetId=" + id.gadgetId + " host=" + id.hostPackage
|
|
+ " provider=" + (id.info == null ? "null" : id.info.provider));
|
|
}
|
|
}
|
|
}
|
|
|
|
public int allocateGadgetId(String hostPackage) {
|
|
synchronized (mGadgetIds) {
|
|
// TODO: Check for pick permission
|
|
int gadgetId = mNextGadgetId++;
|
|
|
|
GadgetId id = new GadgetId();
|
|
id.gadgetId = gadgetId;
|
|
id.hostPackage = hostPackage;
|
|
|
|
mGadgetIds.add(id);
|
|
|
|
return gadgetId;
|
|
}
|
|
}
|
|
|
|
public void deleteGadgetId(int gadgetId) {
|
|
synchronized (mGadgetIds) {
|
|
String callingPackage = getCallingPackage();
|
|
final int N = mGadgetIds.size();
|
|
for (int i=0; i<N; i++) {
|
|
GadgetId id = mGadgetIds.get(i);
|
|
if (canAccessGadgetId(id, callingPackage)) {
|
|
mGadgetIds.remove(i);
|
|
// TODO: Notify someone?
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void bindGadgetId(int gadgetId, ComponentName provider) {
|
|
synchronized (mGadgetIds) {
|
|
GadgetId id = lookupGadgetIdLocked(gadgetId);
|
|
if (id == null) {
|
|
throw new IllegalArgumentException("bad gadgetId"); // TODO: use a better exception
|
|
}
|
|
if (id.info != null) {
|
|
throw new IllegalArgumentException("gadgetId " + gadgetId + " already bound to "
|
|
+ id.info.provider);
|
|
}
|
|
GadgetInfo info = lookupGadgetInfoLocked(provider);
|
|
if (info == null) {
|
|
throw new IllegalArgumentException("not a gadget provider: " + provider);
|
|
}
|
|
|
|
id.info = info;
|
|
}
|
|
}
|
|
|
|
public GadgetInfo getGadgetInfo(int gadgetId) {
|
|
synchronized (mGadgetIds) {
|
|
GadgetId id = lookupGadgetIdLocked(gadgetId);
|
|
if (id != null) {
|
|
return id.info;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public List<GadgetInfo> getInstalledProviders() {
|
|
synchronized (mGadgetIds) {
|
|
return new ArrayList<GadgetInfo>(mInstalledProviders);
|
|
}
|
|
}
|
|
|
|
boolean canAccessGadgetId(GadgetId id, String callingPackage) {
|
|
if (id.hostPackage.equals(callingPackage)) {
|
|
return true;
|
|
}
|
|
if (id.info != null && id.info.provider.getPackageName().equals(callingPackage)) {
|
|
return true;
|
|
}
|
|
// TODO: Check for the pick permission
|
|
//if (has permission) {
|
|
// return true;
|
|
//}
|
|
//return false;
|
|
return true;
|
|
}
|
|
|
|
private GadgetId lookupGadgetIdLocked(int gadgetId) {
|
|
String callingPackage = getCallingPackage();
|
|
final int N = mGadgetIds.size();
|
|
for (int i=0; i<N; i++) {
|
|
GadgetId id = mGadgetIds.get(i);
|
|
if (canAccessGadgetId(id, callingPackage)) {
|
|
return id;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
GadgetInfo lookupGadgetInfoLocked(ComponentName provider) {
|
|
final int N = mInstalledProviders.size();
|
|
for (int i=0; i<N; i++) {
|
|
GadgetInfo info = mInstalledProviders.get(i);
|
|
if (info.provider.equals(provider)) {
|
|
return info;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
ArrayList<GadgetInfo> getGadgetList() {
|
|
PackageManager pm = mPackageManager;
|
|
|
|
// TODO: We have these as different actions. I wonder if it makes more sense to
|
|
// have like a GADGET_ACTION, and then subcommands. It's kind of arbitrary that
|
|
// we look for GADGET_UPDATE_ACTION and not any of the other gadget actions.
|
|
Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION);
|
|
List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
|
|
PackageManager.GET_META_DATA);
|
|
|
|
ArrayList<GadgetInfo> result = new ArrayList<GadgetInfo>();
|
|
|
|
final int N = broadcastReceivers.size();
|
|
for (int i=0; i<N; i++) {
|
|
ResolveInfo ri = broadcastReceivers.get(i);
|
|
ActivityInfo ai = ri.activityInfo;
|
|
GadgetInfo gi = parseGadgetInfoXml(new ComponentName(ai.packageName, ai.name),
|
|
ri.activityInfo);
|
|
if (gi != null) {
|
|
result.add(gi);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private GadgetInfo parseGadgetInfoXml(ComponentName component,
|
|
PackageItemInfo packageItemInfo) {
|
|
GadgetInfo gi = null;
|
|
|
|
XmlResourceParser parser = null;
|
|
try {
|
|
parser = packageItemInfo.loadXmlMetaData(mPackageManager,
|
|
GadgetManager.GADGET_PROVIDER_META_DATA);
|
|
if (parser == null) {
|
|
Log.w(TAG, "No " + GadgetManager.GADGET_PROVIDER_META_DATA + " meta-data for "
|
|
+ "gadget provider '" + component + '\'');
|
|
return null;
|
|
}
|
|
|
|
AttributeSet attrs = Xml.asAttributeSet(parser);
|
|
|
|
int type;
|
|
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
|
|
&& type != XmlPullParser.START_TAG) {
|
|
// drain whitespace, comments, etc.
|
|
}
|
|
|
|
String nodeName = parser.getName();
|
|
if (!"gadget-provider".equals(nodeName)) {
|
|
Log.w(TAG, "Meta-data does not start with gadget-provider tag for"
|
|
+ " gadget provider '" + component + '\'');
|
|
return null;
|
|
}
|
|
|
|
gi = new GadgetInfo();
|
|
|
|
gi.provider = component;
|
|
|
|
TypedArray sa = mContext.getResources().obtainAttributes(attrs,
|
|
com.android.internal.R.styleable.GadgetProviderInfo);
|
|
gi.minWidth = sa.getDimensionPixelSize(
|
|
com.android.internal.R.styleable.GadgetProviderInfo_minWidth, 0);
|
|
gi.minHeight = sa.getDimensionPixelSize(
|
|
com.android.internal.R.styleable.GadgetProviderInfo_minHeight, 0);
|
|
gi.updatePeriodMillis = sa.getInt(
|
|
com.android.internal.R.styleable.GadgetProviderInfo_updatePeriodMillis, 0);
|
|
gi.initialLayout = sa.getResourceId(
|
|
com.android.internal.R.styleable.GadgetProviderInfo_initialLayout, 0);
|
|
sa.recycle();
|
|
} catch (Exception e) {
|
|
// Ok to catch Exception here, because anything going wrong because
|
|
// of what a client process passes to us should not be fatal for the
|
|
// system process.
|
|
Log.w(TAG, "XML parsing failed for gadget provider '" + component + '\'', e);
|
|
} finally {
|
|
if (parser != null) parser.close();
|
|
}
|
|
return gi;
|
|
}
|
|
|
|
void sendEnabled(ComponentName provider) {
|
|
Intent intent = new Intent(GadgetManager.GADGET_ENABLE_ACTION);
|
|
intent.setComponent(provider);
|
|
mContext.sendBroadcast(intent);
|
|
}
|
|
|
|
String getCallingPackage() {
|
|
return mPackageManager.getNameForUid(getCallingUid());
|
|
}
|
|
}
|
|
|