| /* |
| * Copyright (C) 2010 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.smspush; |
| |
| import android.app.Service; |
| import android.content.ActivityNotFoundException; |
| import android.content.ComponentName; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.PackageManager; |
| import android.database.Cursor; |
| import android.database.sqlite.SQLiteOpenHelper; |
| import android.database.sqlite.SQLiteDatabase; |
| import android.os.IBinder; |
| import android.os.RemoteException; |
| import android.util.Log; |
| |
| import com.android.internal.telephony.IWapPushManager; |
| import com.android.internal.telephony.WapPushManagerParams; |
| |
| /** |
| * The WapPushManager service is implemented to process incoming |
| * WAP Push messages and to maintain the Receiver Application/Application |
| * ID mapping. The WapPushManager runs as a system service, and only the |
| * WapPushManager can update the WAP Push message and Receiver Application |
| * mapping (Application ID Table). When the device receives an SMS WAP Push |
| * message, the WapPushManager looks up the Receiver Application name in |
| * Application ID Table. If an application is found, the application is |
| * launched using its full component name instead of broadcasting an implicit |
| * Intent. If a Receiver Application is not found in the Application ID |
| * Table or the WapPushManager returns a process-further value, the |
| * telephony stack will process the message using existing message processing |
| * flow, and broadcast an implicit Intent. |
| */ |
| public class WapPushManager extends Service { |
| |
| private static final String LOG_TAG = "WAP PUSH"; |
| private static final String DATABASE_NAME = "wappush.db"; |
| private static final String APPID_TABLE_NAME = "appid_tbl"; |
| |
| /** |
| * Version number must be incremented when table structure is changed. |
| */ |
| private static final int WAP_PUSH_MANAGER_VERSION = 1; |
| private static final boolean DEBUG_SQL = false; |
| private static final boolean LOCAL_LOGV = false; |
| |
| /** |
| * Inner class that deals with application ID table |
| */ |
| private class WapPushManDBHelper extends SQLiteOpenHelper { |
| WapPushManDBHelper(Context context) { |
| super(context, DATABASE_NAME, null, WAP_PUSH_MANAGER_VERSION); |
| if (LOCAL_LOGV) Log.v(LOG_TAG, "helper instance created."); |
| } |
| |
| @Override |
| public void onCreate(SQLiteDatabase db) { |
| if (LOCAL_LOGV) Log.v(LOG_TAG, "db onCreate."); |
| String sql = "CREATE TABLE " + APPID_TABLE_NAME + " (" |
| + "id INTEGER PRIMARY KEY, " |
| + "x_wap_application TEXT, " |
| + "content_type TEXT, " |
| + "package_name TEXT, " |
| + "class_name TEXT, " |
| + "app_type INTEGER, " |
| + "need_signature INTEGER, " |
| + "further_processing INTEGER, " |
| + "install_order INTEGER " |
| + ")"; |
| |
| if (DEBUG_SQL) Log.v(LOG_TAG, "sql: " + sql); |
| db.execSQL(sql); |
| } |
| |
| @Override |
| public void onUpgrade(SQLiteDatabase db, |
| int oldVersion, int newVersion) { |
| // TODO: when table structure is changed, need to dump and restore data. |
| /* |
| db.execSQL( |
| "drop table if exists "+APPID_TABLE_NAME); |
| onCreate(db); |
| */ |
| Log.w(LOG_TAG, "onUpgrade is not implemented yet. do nothing."); |
| } |
| |
| protected class queryData { |
| public String packageName; |
| public String className; |
| int appType; |
| int needSignature; |
| int furtherProcessing; |
| int installOrder; |
| } |
| |
| /** |
| * Query the latest receiver application info with supplied application ID and |
| * content type. |
| * @param app_id application ID to look up |
| * @param content_type content type to look up |
| */ |
| protected queryData queryLastApp(SQLiteDatabase db, |
| String app_id, String content_type) { |
| if (LOCAL_LOGV) Log.v(LOG_TAG, "queryLastApp app_id: " + app_id |
| + " content_type: " + content_type); |
| |
| Cursor cur = db.query(APPID_TABLE_NAME, |
| new String[] {"install_order", "package_name", "class_name", |
| "app_type", "need_signature", "further_processing"}, |
| "x_wap_application=? and content_type=?", |
| new String[] {app_id, content_type}, |
| null /* groupBy */, |
| null /* having */, |
| "install_order desc" /* orderBy */); |
| |
| queryData ret = null; |
| |
| if (cur.moveToNext()) { |
| ret = new queryData(); |
| ret.installOrder = cur.getInt(cur.getColumnIndex("install_order")); |
| ret.packageName = cur.getString(cur.getColumnIndex("package_name")); |
| ret.className = cur.getString(cur.getColumnIndex("class_name")); |
| ret.appType = cur.getInt(cur.getColumnIndex("app_type")); |
| ret.needSignature = cur.getInt(cur.getColumnIndex("need_signature")); |
| ret.furtherProcessing = cur.getInt(cur.getColumnIndex("further_processing")); |
| } |
| cur.close(); |
| return ret; |
| } |
| |
| } |
| |
| /** |
| * The exported API implementations class |
| */ |
| private class IWapPushManagerStub extends IWapPushManager.Stub { |
| public Context mContext; |
| |
| public IWapPushManagerStub() { |
| |
| } |
| |
| /** |
| * Compare the package signature with WapPushManager package |
| */ |
| protected boolean signatureCheck(String package_name) { |
| PackageManager pm = mContext.getPackageManager(); |
| int match = pm.checkSignatures(mContext.getPackageName(), package_name); |
| |
| if (LOCAL_LOGV) Log.v(LOG_TAG, "compare signature " + mContext.getPackageName() |
| + " and " + package_name + ", match=" + match); |
| |
| return match == PackageManager.SIGNATURE_MATCH; |
| } |
| |
| /** |
| * Returns the status value of the message processing. |
| * The message will be processed as follows: |
| * 1.Look up Application ID Table with x-wap-application-id + content type |
| * 2.Check the signature of package name that is found in the |
| * Application ID Table by using PackageManager.checkSignature |
| * 3.Trigger the Application |
| * 4.Returns the process status value. |
| */ |
| public int processMessage(String app_id, String content_type, Intent intent) |
| throws RemoteException { |
| Log.d(LOG_TAG, "wpman processMsg " + app_id + ":" + content_type); |
| |
| WapPushManDBHelper dbh = getDatabase(mContext); |
| SQLiteDatabase db = dbh.getReadableDatabase(); |
| WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, app_id, content_type); |
| db.close(); |
| |
| if (lastapp == null) { |
| Log.w(LOG_TAG, "no receiver app found for " + app_id + ":" + content_type); |
| return WapPushManagerParams.APP_QUERY_FAILED; |
| } |
| if (LOCAL_LOGV) Log.v(LOG_TAG, "starting " + lastapp.packageName |
| + "/" + lastapp.className); |
| |
| if (lastapp.needSignature != 0) { |
| if (!signatureCheck(lastapp.packageName)) { |
| return WapPushManagerParams.SIGNATURE_NO_MATCH; |
| } |
| } |
| |
| if (lastapp.appType == WapPushManagerParams.APP_TYPE_ACTIVITY) { |
| //Intent intent = new Intent(Intent.ACTION_MAIN); |
| intent.setClassName(lastapp.packageName, lastapp.className); |
| intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| |
| try { |
| mContext.startActivity(intent); |
| } catch (ActivityNotFoundException e) { |
| Log.w(LOG_TAG, "invalid name " + |
| lastapp.packageName + "/" + lastapp.className); |
| return WapPushManagerParams.INVALID_RECEIVER_NAME; |
| } |
| } else { |
| intent.setClassName(mContext, lastapp.className); |
| intent.setComponent(new ComponentName(lastapp.packageName, |
| lastapp.className)); |
| if (mContext.startService(intent) == null) { |
| Log.w(LOG_TAG, "invalid name " + |
| lastapp.packageName + "/" + lastapp.className); |
| return WapPushManagerParams.INVALID_RECEIVER_NAME; |
| } |
| } |
| |
| return WapPushManagerParams.MESSAGE_HANDLED |
| | (lastapp.furtherProcessing == 1 ? |
| WapPushManagerParams.FURTHER_PROCESSING : 0); |
| } |
| |
| protected boolean appTypeCheck(int app_type) { |
| if (app_type == WapPushManagerParams.APP_TYPE_ACTIVITY || |
| app_type == WapPushManagerParams.APP_TYPE_SERVICE) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Returns true if adding the package succeeded. |
| */ |
| public boolean addPackage(String x_app_id, String content_type, |
| String package_name, String class_name, |
| int app_type, boolean need_signature, boolean further_processing) { |
| WapPushManDBHelper dbh = getDatabase(mContext); |
| SQLiteDatabase db = dbh.getWritableDatabase(); |
| WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type); |
| boolean ret = false; |
| boolean insert = false; |
| int sq = 0; |
| |
| if (!appTypeCheck(app_type)) { |
| Log.w(LOG_TAG, "invalid app_type " + app_type + ". app_type must be " |
| + WapPushManagerParams.APP_TYPE_ACTIVITY + " or " |
| + WapPushManagerParams.APP_TYPE_SERVICE); |
| return false; |
| } |
| |
| if (lastapp == null) { |
| insert = true; |
| sq = 0; |
| } else if (!lastapp.packageName.equals(package_name) || |
| !lastapp.className.equals(class_name)) { |
| insert = true; |
| sq = lastapp.installOrder + 1; |
| } |
| |
| if (insert) { |
| ContentValues values = new ContentValues(); |
| |
| values.put("x_wap_application", x_app_id); |
| values.put("content_type", content_type); |
| values.put("package_name", package_name); |
| values.put("class_name", class_name); |
| values.put("app_type", app_type); |
| values.put("need_signature", need_signature ? 1 : 0); |
| values.put("further_processing", further_processing ? 1 : 0); |
| values.put("install_order", sq); |
| db.insert(APPID_TABLE_NAME, null, values); |
| if (LOCAL_LOGV) Log.v(LOG_TAG, "add:" + x_app_id + ":" + content_type |
| + " " + package_name + "." + class_name |
| + ", newsq:" + sq); |
| ret = true; |
| } |
| |
| db.close(); |
| |
| return ret; |
| } |
| |
| /** |
| * Returns true if updating the package succeeded. |
| */ |
| public boolean updatePackage(String x_app_id, String content_type, |
| String package_name, String class_name, |
| int app_type, boolean need_signature, boolean further_processing) { |
| |
| if (!appTypeCheck(app_type)) { |
| Log.w(LOG_TAG, "invalid app_type " + app_type + ". app_type must be " |
| + WapPushManagerParams.APP_TYPE_ACTIVITY + " or " |
| + WapPushManagerParams.APP_TYPE_SERVICE); |
| return false; |
| } |
| |
| WapPushManDBHelper dbh = getDatabase(mContext); |
| SQLiteDatabase db = dbh.getWritableDatabase(); |
| WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type); |
| |
| if (lastapp == null) { |
| db.close(); |
| return false; |
| } |
| |
| ContentValues values = new ContentValues(); |
| String where = "x_wap_application=\'" + x_app_id + "\'" |
| + " and content_type=\'" + content_type + "\'" |
| + " and install_order=" + lastapp.installOrder; |
| |
| values.put("package_name", package_name); |
| values.put("class_name", class_name); |
| values.put("app_type", app_type); |
| values.put("need_signature", need_signature ? 1 : 0); |
| values.put("further_processing", further_processing ? 1 : 0); |
| |
| int num = db.update(APPID_TABLE_NAME, values, where, null); |
| if (LOCAL_LOGV) Log.v(LOG_TAG, "update:" + x_app_id + ":" + content_type + " " |
| + package_name + "." + class_name |
| + ", sq:" + lastapp.installOrder); |
| |
| db.close(); |
| |
| return num > 0; |
| } |
| |
| /** |
| * Returns true if deleting the package succeeded. |
| */ |
| public boolean deletePackage(String x_app_id, String content_type, |
| String package_name, String class_name) { |
| WapPushManDBHelper dbh = getDatabase(mContext); |
| SQLiteDatabase db = dbh.getWritableDatabase(); |
| String where = "x_wap_application=\'" + x_app_id + "\'" |
| + " and content_type=\'" + content_type + "\'" |
| + " and package_name=\'" + package_name + "\'" |
| + " and class_name=\'" + class_name + "\'"; |
| int num_removed = db.delete(APPID_TABLE_NAME, where, null); |
| |
| db.close(); |
| if (LOCAL_LOGV) Log.v(LOG_TAG, "deleted " + num_removed + " rows:" |
| + x_app_id + ":" + content_type + " " |
| + package_name + "." + class_name); |
| return num_removed > 0; |
| } |
| }; |
| |
| |
| /** |
| * Linux IPC Binder |
| */ |
| private final IWapPushManagerStub mBinder = new IWapPushManagerStub(); |
| |
| /** |
| * Default constructor |
| */ |
| public WapPushManager() { |
| super(); |
| mBinder.mContext = this; |
| } |
| |
| @Override |
| public IBinder onBind(Intent arg0) { |
| return mBinder; |
| } |
| |
| /** |
| * Application ID database instance |
| */ |
| private WapPushManDBHelper mDbHelper = null; |
| protected WapPushManDBHelper getDatabase(Context context) { |
| if (mDbHelper == null) { |
| if (LOCAL_LOGV) Log.v(LOG_TAG, "create new db inst."); |
| mDbHelper = new WapPushManDBHelper(context); |
| } |
| return mDbHelper; |
| } |
| |
| |
| /** |
| * This method is used for testing |
| */ |
| public boolean verifyData(String x_app_id, String content_type, |
| String package_name, String class_name, |
| int app_type, boolean need_signature, boolean further_processing) { |
| WapPushManDBHelper dbh = getDatabase(this); |
| SQLiteDatabase db = dbh.getReadableDatabase(); |
| WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type); |
| |
| if (LOCAL_LOGV) Log.v(LOG_TAG, "verifyData app id: " + x_app_id + " content type: " + |
| content_type + " lastapp: " + lastapp); |
| |
| db.close(); |
| |
| if (lastapp == null) return false; |
| |
| if (LOCAL_LOGV) Log.v(LOG_TAG, "verifyData lastapp.packageName: " + lastapp.packageName + |
| " lastapp.className: " + lastapp.className + |
| " lastapp.appType: " + lastapp.appType + |
| " lastapp.needSignature: " + lastapp.needSignature + |
| " lastapp.furtherProcessing: " + lastapp.furtherProcessing); |
| |
| |
| if (lastapp.packageName.equals(package_name) |
| && lastapp.className.equals(class_name) |
| && lastapp.appType == app_type |
| && lastapp.needSignature == (need_signature ? 1 : 0) |
| && lastapp.furtherProcessing == (further_processing ? 1 : 0)) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * This method is used for testing |
| */ |
| public boolean isDataExist(String x_app_id, String content_type, |
| String package_name, String class_name) { |
| WapPushManDBHelper dbh = getDatabase(this); |
| SQLiteDatabase db = dbh.getReadableDatabase(); |
| boolean ret = dbh.queryLastApp(db, x_app_id, content_type) != null; |
| |
| db.close(); |
| return ret; |
| } |
| |
| } |
| |