项目中遇到客户要求点击应用无法启动,并且提示该应用已被禁止运行,下面就介绍一下如何实现这个功能,当然自定义service是少不了的,我只把主要的几个列出,自定义service其他相关的文件,我就不一一列出了.
1,在frameworks/base/core/java/android/app/customized/ICustomizedService.aidl中定义接口,定义的四个接口依次是
添加禁止运行应用名单,删除禁止运行应用名单,获取禁止运行应用名单,显示禁止运行的提示.
void addDisallowedRunningApp(inout List packageNames);
void removeDisallowedRunningApp(inout List packageNames);
List getDisallowedRunningApp();
void showToast();
定义接口出现了inout,简单 说明一下inout,AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的参数为空的对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动,我为了省事,都定义为inout了.
2,在frameworks/base/core/java/android/app/customized/CustomizedManager.java定义接口实现
package android.app.customized;
import android.util.Log;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.RemoteException;
import android.provider.Settings;
import java.io.IOException;
import android.os.ServiceManager;
import android.os.IBinder;
import java.util.List;
import android.app.ActivityManager;
import android.graphics.Bitmap;
public class CustomizedManager{
private static final String TAG="CustomizedManager";
private static final boolean DBG=true;
private static ICustomizedService mService;
private final Context mContext;
public CustomizedManager(Context context){
mContext = context;
mService = ICustomizedService.Stub.asInterface(
ServiceManager.getService("customized"));
}
private static ICustomizedService getService(){
if (mService != null) {
return mService;
}
IBinder b = ServiceManager.getService("customized");
mService = ICustomizedService.Stub.asInterface(b);
return mService;
}
public void addDisallowedRunningApp(List packageNames){
if(DEBUG)Log.d(TAG,"addDisallowedRunningApp");
try {
getService().addDisallowedRunningApp(packageNames);
} catch (RemoteException e) {}
}
public void removeDisallowedRunningApp(List packageNames){
if(DEBUG)Log.d(TAG,"removeDisallowedRunningApp");
try {
getService().removeDisallowedRunningApp(packageNames);
} catch (RemoteException e) {}
}
public List getDisallowedRunningApp(){
if(DEBUG)Log.d(TAG,"getDisallowedRunningApp");
try {
return getService().getDisallowedRunningApp();
} catch (RemoteException e) {}
return null;
}
public void showToast(){
if(DEBUG)Log.d(TAG,"showToast");
ICustomizedService service = getService();
try {
service.showToast();
} catch (RemoteException e) {}
}
}
3,frameworks/base/services/core/java/com/android/server/customized/CustomizedService.java文件,实现AIDL文件中定义的所有接口.
package com.android.server.customized;
import android.os.IBinder;
import android.os.ServiceManager;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.app.customized.ICustomizedService;
import android.content.BroadcastReceiver;
import java.util.List;
import java.util.ArrayList;
import android.content.ContentValues;
import com.android.server.SystemService;
import android.content.BroadcastReceiver;
import android.content.IntentFilter;
import java.util.Timer;
import java.util.TimerTask;
import android.content.ContentResolver;
import java.util.HashSet;
import android.content.pm.PackageInstaller;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
import android.content.IIntentSender;
import android.content.IIntentReceiver;
import android.content.IntentSender;
public class CustomizedService extends ICustomizedService.Stub {
private static final String TAG = "CustomizedService ";
private Context mContext;
private Uri mUri = Telephony.Carriers.CONTENT_URI;
private static final String PREFERRED_APN_URI = "content://telephony/carriers/preferapn";
private ContentResolver mContentResolver;
public static final String WEBSITE = "website";
public static final String IS_WHITE = "is_white";
public static final String WEBSITE_ID = "ws_id";
public final static String AUTHORITY = "com.android.providers.settings.MyControl";
public final static Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/controlbroswer");
public static List appList = new ArrayList();
public static List markUrlList = new ArrayList();
public static List btpairlList = new ArrayList();
public static List noUninstallList = new ArrayList();
public static List wifiList = new ArrayList();
public static List controlBarList = new ArrayList();
public static class Lifecycle extends SystemService {
private CustomizedService mService;
public Lifecycle(Context context) {
super(context);
}
@Override
public void onStart() {
mService = new CustomizedService (getContext());
publishBinderService(Context.CUSTOMIZED, mService);
}
@Override
public void onBootPhase(int phase) {
}
@Override
public void onUnlockUser(int userHandle) {
}
}
public CustomizedService (Context context) {
mContext = context;
}
public void setDisallowedUninstallPackages(List packageNames){
Binder.clearCallingIdentity();
clearControlBrowserUrl("nodel",false);
String ISWHITE = "nodel";
packageNames = delRepeat(packageNames);
if(packageNames != null){
int size = packageNames.size();
for(int i =0; i < size; i++){
ContentValues values = new ContentValues();
values.put(WEBSITE_ID, i);
values.put(IS_WHITE,ISWHITE);
values.put(WEBSITE, packageNames.get(i));
mContentResolver.insert(CONTENT_URI, values);
}
updateMarkUrl();
}
Binder.restoreCallingIdentity(DUMP_TRANSACTION);
}
public void addDisallowedUninstallPackages(List packageNames){
if(packageNames == null)return;
for(int j=0;j packageNames){
if(noUninstallList == null || packageNames == null){
Log.d(TAG,"appList listUrl is null");
return;
}
if(noUninstallList.size() < 1){
updateMarkUrl();
}
if(noUninstallList.size() < 1){
Log.d(TAG,"noUninstallList.size() < 1");
return;
}
for(int i=0;i getDisallowedUninstallPackageList(){
if(noUninstallList == null){
Log.d(TAG,"noUninstallList listUrl is null");
return null;
}
if(noUninstallList.size() < 1){
updateMarkUrl();
}
delRepeat(noUninstallList);
return noUninstallList;
}
private void sentControl(String action, String key, boolean iscontrol) {
long jh = Binder.clearCallingIdentity();
Intent i = new Intent();
i.setAction(action);
if (iscontrol) {
i.putExtra(key, true);
} else {
i.putExtra(key, false);
}
mContext.sendBroadcast(i);
sentControl (action,iscontrol);
Binder.restoreCallingIdentity(jh);
}
private void sentControl(String action, boolean iscontrol) {
long jh = Binder.clearCallingIdentity();
int key;
if (iscontrol) {
key = 0;
} else {
key = 1;
}
Log.i ("custom",",action "+ action + "; key"+key);
Settings.Secure.putInt(mContext.getContentResolver(),action,key);
Binder.restoreCallingIdentity(jh);
}
public void clearControlBrowserUrl(String which){
Binder.clearCallingIdentity();
String[] selectionArgs = new String[]{which};
String selection = "" + IS_WHITE + "=?";
mContentResolver.delete(CONTENT_URI,selection,selectionArgs);
Binder.restoreCallingIdentity(DUMP_TRANSACTION);
}
public void updateMarkUrl(){
appList.clear();
markUrlList.clear();
btpairlList.clear();
noUninstallList.clear();
wifiList.clear();
controlBarList.clear();
Cursor cursor = mContentResolver.query(CONTENT_URI, null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
int index[] = new int[] { cursor.getColumnIndex(WEBSITE_ID),
cursor.getColumnIndex(IS_WHITE),
cursor.getColumnIndex(WEBSITE) };
do {
if (cursor.getString(index[1]).equals("APP")){
appList.add(cursor.getString(index[2]));
} else if (cursor.getString(index[1]).equals("IPL")) {
markUrlList.add(cursor.getString(index[2]));
} else if (cursor.getString(index[1]).equals("BT")) {
btpairlList.add(cursor.getString(index[2]));
} else if (cursor.getString(index[1]).equals("NODEL")) {
noUninstallList.add(cursor.getString(index[2]));
} else if (cursor.getString(index[1]).equals("WIFI")) {
wifiList.add(cursor.getString(index[2]));
} else if (cursor.getString(index[1]).equals("NOTI")) {
controlBarList.add(cursor.getString(index[2]));
}
} while (cursor.moveToNext());
}
cursor.close();
}
private List delRepeat(List list){
if(list == null)return null;
if(list.size() > 1){
HashSet h = new HashSet(list);
list.clear();
list.addAll(h);
}
return list;
}
private boolean getControl(String action){
return Settings.Secure.getInt(mContext.getContentResolver(), action, 1) == 0;
}
}
4,在frameworks/base/packages/SettingsProvider/AndroidManifest.xml添加provider
5,定义frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/MyControl.java,这个文件对数据库增删改查的方法进行定义,
package com.android.providers.settings;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.net.Uri;
import android.util.Log;
public class MyControl extends ContentProvider {
public static final String ID = "_id";
public static final String WEBSITE_ID = "ws_id";
public static final String WEBSITE = "website";
public static final String IS_WHITE = "is_white";
public static final String DATABASE_NAME = "mycontrol.db";
private static final String TABLE_NAME = "controlbroswer";
public final static String AUTHORITY = "com.android.providers.settings.MyControl";
public final static Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/controlbroswer");
private static final int DB_VERSION = 2;
private SQLiteDatabase sqDb;
private SqliteHelper helper;
private static final String DATABASE_CREATE = "Create table " + TABLE_NAME
+ "( " + ID + " integer primary key autoincrement,"
+ WEBSITE_ID + " TEXT,"
+ IS_WHITE + " TEXT,"
+ WEBSITE + " TEXT);";
class SqliteHelper extends SQLiteOpenHelper {
public SqliteHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS" + TABLE_NAME);
onCreate(db);
}
}
@Override
public boolean onCreate() {
helper = new SqliteHelper(getContext(), DATABASE_NAME, null, DB_VERSION);
return helper == null ? false : true;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
sqDb = helper.getWritableDatabase();
int count = sqDb.delete(TABLE_NAME, selection, selectionArgs);
return count;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
sqDb = helper.getWritableDatabase();
long rowid = sqDb.insert(TABLE_NAME, WEBSITE_ID, values);
Uri insertUri = ContentUris.withAppendedId(uri, rowid);
return insertUri;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
sqDb = helper.getWritableDatabase();
Cursor cursor = sqDb.query(TABLE_NAME, projection, selection,
selectionArgs, null, null, sortOrder);
return cursor;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
sqDb = helper.getWritableDatabase();
int count = sqDb.update(TABLE_NAME, values, selection, selectionArgs);
return count;
}
@Override
public String getType(Uri uri) {
return "vnd.android.cursor.dir/controlbroswer";
}
}
6,在frameworks/base/core/java/android/app/ActivityThread.java 文件中对启动应用进行拦截,拦截的地方有两个,分别是launch时调用handleLaunchActivity方法和resume时调用performResumeActivity方法.
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
CustomizedManager cm = new CustomizedManager(r.activity);
if(cm.getDisallowedRunningApp() != null && cm.getDisallowedRunningApp().size() > 0){
if(cm.getDisallowedRunningApp().contains(r.intent.getComponent().getPackageName())){
try {
cm.showToast();
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
} catch (RemoteException ex) {}
}
}
......
public final ActivityClientRecord performResumeActivity(IBinder token,
boolean clearHide) {
ActivityClientRecord r = mActivities.get(token);
CustomizedManager cm = new CustomizedManager(r.activity);
if(cm.getDisallowedRunningApp() != null && cm.getDisallowedRunningApp().size() > 0){
if(cm.getDisallowedRunningApp().contains(r.intent.getComponent().getPackageName())){
try {
cm.showToast();
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
} catch (RemoteException ex) {}
}
}
......