• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.smspush;
18 
19 import android.app.Service;
20 import android.content.ActivityNotFoundException;
21 import android.content.ComponentName;
22 import android.content.ContentValues;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.PackageManager;
26 import android.database.Cursor;
27 import android.database.sqlite.SQLiteOpenHelper;
28 import android.database.sqlite.SQLiteDatabase;
29 import android.os.IBinder;
30 import android.os.RemoteException;
31 import android.util.Log;
32 
33 import com.android.internal.telephony.IWapPushManager;
34 import com.android.internal.telephony.WapPushManagerParams;
35 
36 /**
37  * The WapPushManager service is implemented to process incoming
38  * WAP Push messages and to maintain the Receiver Application/Application
39  * ID mapping. The WapPushManager runs as a system service, and only the
40  * WapPushManager can update the WAP Push message and Receiver Application
41  * mapping (Application ID Table). When the device receives an SMS WAP Push
42  * message, the WapPushManager looks up the Receiver Application name in
43  * Application ID Table. If an application is found, the application is
44  * launched using its full component name instead of broadcasting an implicit
45  * Intent. If a Receiver Application is not found in the Application ID
46  * Table or the WapPushManager returns a process-further value, the
47  * telephony stack will process the message using existing message processing
48  * flow, and broadcast an implicit Intent.
49  */
50 public class WapPushManager extends Service {
51 
52     private static final String LOG_TAG = "WAP PUSH";
53     private static final String DATABASE_NAME = "wappush.db";
54     private static final String APPID_TABLE_NAME = "appid_tbl";
55 
56     /**
57      * Version number must be incremented when table structure is changed.
58      */
59     private static final int WAP_PUSH_MANAGER_VERSION = 1;
60     private static final boolean DEBUG_SQL = false;
61     private static final boolean LOCAL_LOGV = false;
62 
63     /**
64      * Inner class that deals with application ID table
65      */
66     private class WapPushManDBHelper extends SQLiteOpenHelper {
WapPushManDBHelper(Context context)67         WapPushManDBHelper(Context context) {
68             super(context, DATABASE_NAME, null, WAP_PUSH_MANAGER_VERSION);
69             if (LOCAL_LOGV) Log.v(LOG_TAG, "helper instance created.");
70         }
71 
72         @Override
onCreate(SQLiteDatabase db)73         public void onCreate(SQLiteDatabase db) {
74             if (LOCAL_LOGV) Log.v(LOG_TAG, "db onCreate.");
75             String sql = "CREATE TABLE " + APPID_TABLE_NAME + " ("
76                     + "id INTEGER PRIMARY KEY, "
77                     + "x_wap_application TEXT, "
78                     + "content_type TEXT, "
79                     + "package_name TEXT, "
80                     + "class_name TEXT, "
81                     + "app_type INTEGER, "
82                     + "need_signature INTEGER, "
83                     + "further_processing INTEGER, "
84                     + "install_order INTEGER "
85                     + ")";
86 
87             if (DEBUG_SQL) Log.v(LOG_TAG, "sql: " + sql);
88             db.execSQL(sql);
89         }
90 
91         @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)92         public void onUpgrade(SQLiteDatabase db,
93                     int oldVersion, int newVersion) {
94             // TODO: when table structure is changed, need to dump and restore data.
95             /*
96               db.execSQL(
97               "drop table if exists "+APPID_TABLE_NAME);
98               onCreate(db);
99             */
100             Log.w(LOG_TAG, "onUpgrade is not implemented yet. do nothing.");
101         }
102 
103         protected class queryData {
104             public String packageName;
105             public String className;
106             int appType;
107             int needSignature;
108             int furtherProcessing;
109             int installOrder;
110         }
111 
112         /**
113          * Query the latest receiver application info with supplied application ID and
114          * content type.
115          * @param app_id    application ID to look up
116          * @param content_type    content type to look up
117          */
queryLastApp(SQLiteDatabase db, String app_id, String content_type)118         protected queryData queryLastApp(SQLiteDatabase db,
119                 String app_id, String content_type) {
120             if (LOCAL_LOGV) Log.v(LOG_TAG, "queryLastApp app_id: " + app_id
121                     + " content_type: " +  content_type);
122 
123             Cursor cur = db.query(APPID_TABLE_NAME,
124                     new String[] {"install_order", "package_name", "class_name",
125                     "app_type", "need_signature", "further_processing"},
126                     "x_wap_application=? and content_type=?",
127                     new String[] {app_id, content_type},
128                     null /* groupBy */,
129                     null /* having */,
130                     "install_order desc" /* orderBy */);
131 
132             queryData ret = null;
133 
134             if (cur.moveToNext()) {
135                 ret = new queryData();
136                 ret.installOrder = cur.getInt(cur.getColumnIndex("install_order"));
137                 ret.packageName = cur.getString(cur.getColumnIndex("package_name"));
138                 ret.className = cur.getString(cur.getColumnIndex("class_name"));
139                 ret.appType = cur.getInt(cur.getColumnIndex("app_type"));
140                 ret.needSignature = cur.getInt(cur.getColumnIndex("need_signature"));
141                 ret.furtherProcessing = cur.getInt(cur.getColumnIndex("further_processing"));
142             }
143             cur.close();
144             return ret;
145         }
146 
147     }
148 
149     /**
150      * The exported API implementations class
151      */
152     private class IWapPushManagerStub extends IWapPushManager.Stub {
153         public Context mContext;
154 
IWapPushManagerStub()155         public IWapPushManagerStub() {
156 
157         }
158 
159         /**
160          * Compare the package signature with WapPushManager package
161          */
signatureCheck(String package_name)162         protected boolean signatureCheck(String package_name) {
163             PackageManager pm = mContext.getPackageManager();
164             int match = pm.checkSignatures(mContext.getPackageName(), package_name);
165 
166             if (LOCAL_LOGV) Log.v(LOG_TAG, "compare signature " + mContext.getPackageName()
167                     + " and " +  package_name + ", match=" + match);
168 
169             return match == PackageManager.SIGNATURE_MATCH;
170         }
171 
172         /**
173          * Returns the status value of the message processing.
174          * The message will be processed as follows:
175          * 1.Look up Application ID Table with x-wap-application-id + content type
176          * 2.Check the signature of package name that is found in the
177          *   Application ID Table by using PackageManager.checkSignature
178          * 3.Trigger the Application
179          * 4.Returns the process status value.
180          */
processMessage(String app_id, String content_type, Intent intent)181         public int processMessage(String app_id, String content_type, Intent intent)
182             throws RemoteException {
183             Log.d(LOG_TAG, "wpman processMsg " + app_id + ":" + content_type);
184 
185             WapPushManDBHelper dbh = getDatabase(mContext);
186             SQLiteDatabase db = dbh.getReadableDatabase();
187             WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, app_id, content_type);
188             db.close();
189 
190             if (lastapp == null) {
191                 Log.w(LOG_TAG, "no receiver app found for " + app_id + ":" + content_type);
192                 return WapPushManagerParams.APP_QUERY_FAILED;
193             }
194             if (LOCAL_LOGV) Log.v(LOG_TAG, "starting " + lastapp.packageName
195                     + "/" + lastapp.className);
196 
197             if (lastapp.needSignature != 0) {
198                 if (!signatureCheck(lastapp.packageName)) {
199                     return WapPushManagerParams.SIGNATURE_NO_MATCH;
200                 }
201             }
202 
203             if (lastapp.appType == WapPushManagerParams.APP_TYPE_ACTIVITY) {
204                 //Intent intent = new Intent(Intent.ACTION_MAIN);
205                 intent.setClassName(lastapp.packageName, lastapp.className);
206                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
207 
208                 try {
209                     mContext.startActivity(intent);
210                 } catch (ActivityNotFoundException e) {
211                     Log.w(LOG_TAG, "invalid name " +
212                             lastapp.packageName + "/" + lastapp.className);
213                     return WapPushManagerParams.INVALID_RECEIVER_NAME;
214                 }
215             } else {
216                 intent.setClassName(mContext, lastapp.className);
217                 intent.setComponent(new ComponentName(lastapp.packageName,
218                         lastapp.className));
219                 if (mContext.startService(intent) == null) {
220                     Log.w(LOG_TAG, "invalid name " +
221                             lastapp.packageName + "/" + lastapp.className);
222                     return WapPushManagerParams.INVALID_RECEIVER_NAME;
223                 }
224             }
225 
226             return WapPushManagerParams.MESSAGE_HANDLED
227                     | (lastapp.furtherProcessing == 1 ?
228                             WapPushManagerParams.FURTHER_PROCESSING : 0);
229         }
230 
appTypeCheck(int app_type)231         protected boolean appTypeCheck(int app_type) {
232             if (app_type == WapPushManagerParams.APP_TYPE_ACTIVITY ||
233                     app_type == WapPushManagerParams.APP_TYPE_SERVICE) {
234                 return true;
235             } else {
236                 return false;
237             }
238         }
239 
240         /**
241          * Returns true if adding the package succeeded.
242          */
addPackage(String x_app_id, String content_type, String package_name, String class_name, int app_type, boolean need_signature, boolean further_processing)243         public boolean addPackage(String x_app_id, String content_type,
244                 String package_name, String class_name,
245                 int app_type, boolean need_signature, boolean further_processing) {
246             WapPushManDBHelper dbh = getDatabase(mContext);
247             SQLiteDatabase db = dbh.getWritableDatabase();
248             WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
249             boolean ret = false;
250             boolean insert = false;
251             int sq = 0;
252 
253             if (!appTypeCheck(app_type)) {
254                 Log.w(LOG_TAG, "invalid app_type " + app_type + ". app_type must be "
255                         + WapPushManagerParams.APP_TYPE_ACTIVITY + " or "
256                         + WapPushManagerParams.APP_TYPE_SERVICE);
257                 return false;
258             }
259 
260             if (lastapp == null) {
261                 insert = true;
262                 sq = 0;
263             } else if (!lastapp.packageName.equals(package_name) ||
264                     !lastapp.className.equals(class_name)) {
265                 insert = true;
266                 sq = lastapp.installOrder + 1;
267             }
268 
269             if (insert) {
270                 ContentValues values = new ContentValues();
271 
272                 values.put("x_wap_application", x_app_id);
273                 values.put("content_type", content_type);
274                 values.put("package_name", package_name);
275                 values.put("class_name", class_name);
276                 values.put("app_type", app_type);
277                 values.put("need_signature", need_signature ? 1 : 0);
278                 values.put("further_processing", further_processing ? 1 : 0);
279                 values.put("install_order", sq);
280                 db.insert(APPID_TABLE_NAME, null, values);
281                 if (LOCAL_LOGV) Log.v(LOG_TAG, "add:" + x_app_id + ":" + content_type
282                         + " " + package_name + "." + class_name
283                         + ", newsq:" + sq);
284                 ret = true;
285             }
286 
287             db.close();
288 
289             return ret;
290         }
291 
292         /**
293          * Returns true if updating the package succeeded.
294          */
updatePackage(String x_app_id, String content_type, String package_name, String class_name, int app_type, boolean need_signature, boolean further_processing)295         public boolean updatePackage(String x_app_id, String content_type,
296                 String package_name, String class_name,
297                 int app_type, boolean need_signature, boolean further_processing) {
298 
299             if (!appTypeCheck(app_type)) {
300                 Log.w(LOG_TAG, "invalid app_type " + app_type + ". app_type must be "
301                         + WapPushManagerParams.APP_TYPE_ACTIVITY + " or "
302                         + WapPushManagerParams.APP_TYPE_SERVICE);
303                 return false;
304             }
305 
306             WapPushManDBHelper dbh = getDatabase(mContext);
307             SQLiteDatabase db = dbh.getWritableDatabase();
308             WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
309 
310             if (lastapp == null) {
311                 db.close();
312                 return false;
313             }
314 
315             ContentValues values = new ContentValues();
316             String where = "x_wap_application=\'" + x_app_id + "\'"
317                     + " and content_type=\'" + content_type + "\'"
318                     + " and install_order=" + lastapp.installOrder;
319 
320             values.put("package_name", package_name);
321             values.put("class_name", class_name);
322             values.put("app_type", app_type);
323             values.put("need_signature", need_signature ? 1 : 0);
324             values.put("further_processing", further_processing ? 1 : 0);
325 
326             int num = db.update(APPID_TABLE_NAME, values, where, null);
327             if (LOCAL_LOGV) Log.v(LOG_TAG, "update:" + x_app_id + ":" + content_type + " "
328                     + package_name + "." + class_name
329                     + ", sq:" + lastapp.installOrder);
330 
331             db.close();
332 
333             return num > 0;
334         }
335 
336         /**
337          * Returns true if deleting the package succeeded.
338          */
deletePackage(String x_app_id, String content_type, String package_name, String class_name)339         public boolean deletePackage(String x_app_id, String content_type,
340                 String package_name, String class_name) {
341             WapPushManDBHelper dbh = getDatabase(mContext);
342             SQLiteDatabase db = dbh.getWritableDatabase();
343             String where = "x_wap_application=\'" + x_app_id + "\'"
344                     + " and content_type=\'" + content_type + "\'"
345                     + " and package_name=\'" + package_name + "\'"
346                     + " and class_name=\'" + class_name + "\'";
347             int num_removed = db.delete(APPID_TABLE_NAME, where, null);
348 
349             db.close();
350             if (LOCAL_LOGV) Log.v(LOG_TAG, "deleted " + num_removed + " rows:"
351                     + x_app_id + ":" + content_type + " "
352                     + package_name + "." + class_name);
353             return num_removed > 0;
354         }
355     };
356 
357 
358     /**
359      * Linux IPC Binder
360      */
361     private final IWapPushManagerStub mBinder = new IWapPushManagerStub();
362 
363     /**
364      * Default constructor
365      */
WapPushManager()366     public WapPushManager() {
367         super();
368         mBinder.mContext = this;
369     }
370 
371     @Override
onBind(Intent arg0)372     public IBinder onBind(Intent arg0) {
373         return mBinder;
374     }
375 
376     /**
377      * Application ID database instance
378      */
379     private WapPushManDBHelper mDbHelper = null;
getDatabase(Context context)380     protected WapPushManDBHelper getDatabase(Context context) {
381         if (mDbHelper == null) {
382             if (LOCAL_LOGV) Log.v(LOG_TAG, "create new db inst.");
383             mDbHelper = new WapPushManDBHelper(context);
384         }
385         return mDbHelper;
386     }
387 
388 
389     /**
390      * This method is used for testing
391      */
verifyData(String x_app_id, String content_type, String package_name, String class_name, int app_type, boolean need_signature, boolean further_processing)392     public boolean verifyData(String x_app_id, String content_type,
393             String package_name, String class_name,
394             int app_type, boolean need_signature, boolean further_processing) {
395         WapPushManDBHelper dbh = getDatabase(this);
396         SQLiteDatabase db = dbh.getReadableDatabase();
397         WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
398 
399         if (LOCAL_LOGV) Log.v(LOG_TAG, "verifyData app id: " + x_app_id + " content type: " +
400                 content_type + " lastapp: " + lastapp);
401 
402         db.close();
403 
404         if (lastapp == null) return false;
405 
406         if (LOCAL_LOGV) Log.v(LOG_TAG, "verifyData lastapp.packageName: " + lastapp.packageName +
407                 " lastapp.className: " + lastapp.className +
408                 " lastapp.appType: " + lastapp.appType +
409                 " lastapp.needSignature: " + lastapp.needSignature +
410                 " lastapp.furtherProcessing: " + lastapp.furtherProcessing);
411 
412 
413         if (lastapp.packageName.equals(package_name)
414                 && lastapp.className.equals(class_name)
415                 && lastapp.appType == app_type
416                 &&  lastapp.needSignature == (need_signature ? 1 : 0)
417                 &&  lastapp.furtherProcessing == (further_processing ? 1 : 0)) {
418             return true;
419         } else {
420             return false;
421         }
422     }
423 
424     /**
425      * This method is used for testing
426      */
isDataExist(String x_app_id, String content_type, String package_name, String class_name)427     public boolean isDataExist(String x_app_id, String content_type,
428             String package_name, String class_name) {
429         WapPushManDBHelper dbh = getDatabase(this);
430         SQLiteDatabase db = dbh.getReadableDatabase();
431         boolean ret = dbh.queryLastApp(db, x_app_id, content_type) != null;
432 
433         db.close();
434         return ret;
435     }
436 
437 }
438 
439