1 /* 2 * Copyright (C) 2014 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.omadm.service; 18 19 import android.app.AlarmManager; 20 import android.app.Notification; 21 import android.app.NotificationManager; 22 import android.app.PendingIntent; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.SharedPreferences; 26 import android.os.Process; 27 import android.content.pm.PackageManager; 28 import android.util.Log; 29 30 import java.io.File; 31 32 final class DMHelper { 33 private static final String TAG = "DMHelper"; 34 private static final boolean DBG = DMClientService.DBG; 35 36 public static final String POSTPONED_DATA_PATH 37 = "/data/data/com.android.omadm.service/shared_prefs/dmpostponed.dat"; 38 39 private static final String FOTA_APN_FILE_PATH 40 = "/data/data/com.android.omadm.service/shared_prefs/fotaapnprefs.xml"; 41 42 public static final int UI_MODE_INFORMATIVE = 2; 43 44 public static final int UI_MODE_CONFIRMATION = 3; 45 46 public static final int NOTIFICATION_INFORMATIVE_ID = 0xcb01fa64; 47 48 public static final int NOTIFICATION_CONFIRMATION_ID = 0xadc19b91; 49 50 51 // ------- Constant VALUES -----------// 52 // message lifetime (24 hours) in milliseconds 53 private static final long MESSAGE_LIFETIME = 24 * 60 * 60 * 1000000000L; //nanoseconds 54 55 // maximum number failures to attempt starting DM session 56 public static final long MAX_SESSION_ATTEMPTS = 3; 57 58 // time before attempt start DM session after a failure (in seconds) 59 public static final int TIME_BETWEEN_SESSION_ATTEMPTS = 30 * 60; //seconds 60 61 // time to check status after starting DM service; try to restart if service will die (in seconds) 62 public static final int TIME_CHECK_STATUS_AFTER_STARTING_DM_SERVICE = 30 * 60; //seconds 63 64 // time to check and repost notification in case if user cancel it; subscribe during posting notification (in seconds) 65 public static final int TIME_CHECK_NOTIFICATION_AFTER_SUBSCRIPTION = 30 * 60; //seconds 66 67 // time to check status after starting call and data monitoring service; try to restart if service will die (in seconds) 68 public static final int TIME_CHECK_STATUS_AFTER_STARTING_MONITORING_SERVICE = 20 * 60; //seconds 69 70 71 // ----- States ----// 72 public static final String STATE_KEY = "dmmsgstate"; 73 74 public static final int STATE_IDLE = 0; // nothing there 75 76 public static final int STATE_PENDING_MESSAGE = 1; // message received and saved 77 78 public static final int STATE_APPROVED_BY_USER = 2; // message approved or silent 79 80 public static final int STATE_SESSION_IN_PROGRESS = 3; // DM session has been started 81 82 83 // ----- Keys ----// 84 // shared properties name 85 public static final String DM_PREFERENCES_KEY = "dmpostponed"; 86 87 // key to keep timestamp for the message in the shared properties. Also used as a request ID 88 public static final String MESSAGE_TIMESTAMP_ID_KEY = "dm_msg_time_init"; 89 90 public static final String MESSAGE_SERVER_URL_KEY = "dm_server_url"; 91 92 public static final String MESSAGE_PROXY_HOSTNAME_KEY = "dm_proxy_hostname"; 93 94 // key to keep number attempts to start DM session 95 public static final String DM_SESSION_ATTEMPTS_KEY = "dm_session_attempts"; 96 97 // key to keep uiMode 98 public static final String DM_UI_MODE_KEY = "dm_ui_mode"; 99 100 // key for pending session type 101 public static final String DM_SESSION_TYPE_KEY = "type"; 102 103 // ---- Key and States used for fota apn ----// 104 // shared property name 105 public static final String FOTA_APN_PREFERENCE_KEY = "fotaapnprefs"; 106 107 public static final String FOTA_APN_STATE_KEY = "fotapnstate"; 108 109 public static final String FOTA_ALERT_STRING_KEY = "fota_alert_string"; 110 111 public static final String FOTA_SERVER_ID_KEY = "fota_server_id"; 112 113 // initial APN monitor state 114 public static final int FOTA_APN_STATE_INIT = 0; // no action needed for this 115 116 // apn switch needed for NI session 117 public static final int FOTA_APN_STATE_START_DM_SESSION = 1; 118 119 // apn switch needed for FDM session 120 public static final int FOTA_APN_STATE_REPORT_DM_SESSION = 2; 121 122 // RPTD states used to decide when to stop using fota apn 123 public static final int FOTA_APN_STATE_START_DM_SESSION_RPTD = 3; 124 public static final int FOTA_APN_STATE_REPORT_DM_SESSION_RPTD = 4; 125 126 // ---- Keys used for storing FDM MSG Values ----// 127 public static final String LAWMO_RESULT_KEY = "lawmoResult"; 128 129 public static final String FOTA_RESULT_KEY = "fotaResult"; 130 131 public static final String PKG_URI_KEY = "pkgURI"; 132 133 public static final String ALERT_TYPE_KEY = "alertType"; 134 135 public static final String CORRELATOR_KEY = "correlator"; 136 137 public static final String SERVER_ID_KEY = "serverID"; 138 139 // --- Key used for storing hostname override for Sprint ---// 140 public static final String SERVER_HOSTNAME_OVERRIDE_KEY = "serverHostname"; 141 142 //--- Keys used for storing imei ---// 143 public static final String IMEI_PREFERENCE_KEY = "imeivalue"; 144 145 public static final String IMEI_VALUE_KEY = "imei"; 146 147 //--- Keys used for storing AKEY ---// 148 public static final String AKEY_PREFERENCE_KEY = "akeyvalue"; 149 150 public static final String AKEY_VALUE_KEY = "akey"; 151 152 private static boolean sfirstTriggerReceived = false; 153 154 // private constructor 155 private DMHelper() {} 156 157 // Subscribing for the Timer Alert 158 public static void subscribeForTimeAlert(Context context, int seconds) { 159 cancelTimeAlert(context); 160 161 logd("subscribeForTimeAlert ..."); 162 163 Intent intent = new Intent(context, DMIntentReceiver.class); 164 intent.setAction(DMIntent.ACTION_TIMER_ALERT); 165 PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0); 166 167 long wakeupTime = System.currentTimeMillis() + (seconds * 1000L); 168 169 // Schedule the alarm 170 AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 171 am.set(AlarmManager.RTC_WAKEUP, wakeupTime, sender); 172 logd("subscribeForTimeAlert for " + seconds + " seconds done!"); 173 } 174 175 // Canceling subscription for time alert 176 private static void cancelTimeAlert(Context context) { 177 logd("cancelTimeAlarm ..."); 178 179 Intent intent = new Intent(context, DMIntentReceiver.class); 180 intent.setAction(DMIntent.ACTION_TIMER_ALERT); 181 PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0); 182 183 AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 184 am.cancel(sender); 185 logd("cancelTimeAlarm done!"); 186 } 187 188 // post confirmation notification when UI mode required user interaction 189 public static void postConfirmationNotification(Context context) { 190 NotificationManager mNotificationManager = (NotificationManager) context 191 .getSystemService(Context.NOTIFICATION_SERVICE); 192 193 CharSequence text = context.getText( 194 R.string.dm_session_confirmation_notification_message) 195 .toString(); 196 197 Intent provisioning = new Intent(context, DMSessionConfirmAlertActivity.class); 198 199 PendingIntent contentIntent = PendingIntent.getActivity(context, 0, provisioning, 0); 200 201 Notification notification = new Notification.Builder(context) 202 .setSmallIcon(R.drawable.alert_dialog_icon) 203 .setTicker(text) 204 .setWhen(System.currentTimeMillis()) 205 .setContentTitle( 206 context.getText(R.string.dm_session_confirmation_notification_label)) 207 .setContentText(text) 208 .setContentIntent(contentIntent) 209 .setColor(context.getResources().getColor( 210 com.android.internal.R.color.system_notification_accent_color)) 211 .build(); 212 213 mNotificationManager.notify(NOTIFICATION_CONFIRMATION_ID, 214 notification); 215 } 216 217 // post informative notification when UI mode required to inform users 218 private static void postInformativeNotification(Context context, int titleId, int textId) { 219 NotificationManager mNotificationManager = (NotificationManager) context 220 .getSystemService(Context.NOTIFICATION_SERVICE); 221 222 CharSequence text = context.getText(textId); 223 224 Intent provisioning = new Intent(context, DMIntentReceiver.class); 225 provisioning.setAction(DMIntent.ACTION_CLOSE_NOTIFICATION_INFO); 226 227 PendingIntent contentIntent = PendingIntent.getBroadcast(context, 0, provisioning, 0); 228 229 Notification notification = new Notification.Builder(context) 230 .setSmallIcon(R.drawable.alert_dialog_icon) 231 .setTicker(text) 232 .setWhen(System.currentTimeMillis()) 233 .setContentTitle(context.getText(titleId)) 234 .setContentText(text) 235 .setContentIntent(contentIntent) 236 .setColor(context.getResources().getColor( 237 com.android.internal.R.color.system_notification_accent_color)) 238 .build(); 239 240 mNotificationManager.notify(NOTIFICATION_INFORMATIVE_ID, notification); 241 } 242 243 // post informative notification when UI mode required to inform users 244 public static void postInformativeNotification_message1(Context context) { 245 postInformativeNotification(context, 246 R.string.dm_session_information_notification_label, 247 R.string.dm_session_information_notification_message1); 248 } 249 250 //post informative notification when UI mode required to inform users 251 public static void postInformativeNotification_message2_success(Context context) { 252 postInformativeNotification(context, 253 R.string.dm_session_information_notification_label, 254 R.string.dm_session_information_notification_message2_success); 255 } 256 257 //post informative notification when UI mode required to inform users 258 public static void postInformativeNotification_message2_fail(Context context) { 259 postInformativeNotification(context, 260 R.string.dm_session_information_notification_label, 261 R.string.dm_session_information_notification_message2_fail); 262 } 263 264 //clear notification 265 public static void cancelNotification(Context context, int notificationId) { 266 NotificationManager nm = (NotificationManager) context 267 .getSystemService(Context.NOTIFICATION_SERVICE); 268 nm.cancel(notificationId); 269 } 270 271 272 //remove message and all from shared properties 273 private static void clearSharedPreferences(Context context) { 274 SharedPreferences p = context.getSharedPreferences(DM_PREFERENCES_KEY, 0); 275 SharedPreferences.Editor ed = p.edit(); 276 ed.clear(); 277 ed.apply(); 278 279 //remove file with message 280 File file = new File(POSTPONED_DATA_PATH); 281 if (file.exists()) { 282 file.delete(); 283 } 284 } 285 286 // set Sprint server URL 287 public static void setServerUrl(Context context, String url) { 288 logd("setServerUrl: " + url); 289 SharedPreferences p = context.getSharedPreferences(SERVER_HOSTNAME_OVERRIDE_KEY, 0); 290 p.edit().putString(MESSAGE_SERVER_URL_KEY, url).apply(); 291 } 292 293 // set Sprint proxy hostname 294 public static void setProxyHostname(Context context, String hostname) { 295 logd("setProxyHostname: " + hostname); 296 SharedPreferences p = context.getSharedPreferences(SERVER_HOSTNAME_OVERRIDE_KEY, 0); 297 p.edit().putString(MESSAGE_PROXY_HOSTNAME_KEY, hostname).apply(); 298 } 299 300 // get Sprint server URL 301 public static String getServerUrl(Context context) { 302 SharedPreferences p = context.getSharedPreferences(SERVER_HOSTNAME_OVERRIDE_KEY, 0); 303 String url = p.getString(MESSAGE_SERVER_URL_KEY, null); 304 logd("getServerUrl: " + url); 305 return url; 306 } 307 308 // get Sprint proxy hostname 309 public static String getProxyHostname(Context context) { 310 SharedPreferences p = context.getSharedPreferences(SERVER_HOSTNAME_OVERRIDE_KEY, 0); 311 String hostname = p.getString(MESSAGE_PROXY_HOSTNAME_KEY, null); 312 logd("getProxyHostname: " + hostname); 313 return hostname; 314 } 315 316 // check if message is expired; compares current time with the timestamp 317 // for the message and its lifetime 318 public static boolean isMessageExpired(Context context) { 319 // get message timestamp from preferences 320 SharedPreferences p = context.getSharedPreferences(DM_PREFERENCES_KEY, 0); 321 long messageTimestamp = p.getLong(MESSAGE_TIMESTAMP_ID_KEY, -1); 322 logd("isMessageExpired: messageTimestamp is " + messageTimestamp); 323 324 return messageTimestamp == -1 || System.nanoTime() > (messageTimestamp 325 + MESSAGE_LIFETIME); 326 } 327 328 // clean all... shared preferences, notifications, time alerts.... 329 public static void cleanAllResources(Context context) { 330 logd("Inside cleanAllResources"); 331 clearSharedPreferences(context); 332 // Not canceling informative notification since otherwise it disappears very quickly (as 333 // soon as DM session ends which is just a few seconds) 334 // cancelNotification(context, NOTIFICATION_INFORMATIVE_ID); 335 cancelNotification(context, NOTIFICATION_CONFIRMATION_ID); 336 cancelTimeAlert(context); 337 } 338 339 // remove message and all from shared properties for fota apn 340 private static void clearFotaApnSharedPreferences(Context context) { 341 logd("Inside clearFotaApnSharedPreferences"); 342 SharedPreferences p = context.getSharedPreferences( 343 FOTA_APN_PREFERENCE_KEY, 0); 344 SharedPreferences.Editor ed = p.edit(); 345 ed.clear(); 346 ed.apply(); 347 348 //remove file with message 349 File file = new File(FOTA_APN_FILE_PATH); 350 if (file.exists()) { 351 logd("fotaapnprefs.xml file deleted"); 352 // FIXME: don't ignore result of delete 353 file.delete(); 354 } 355 } 356 357 public static boolean isRunningAsOwner() { 358 return Process.myUserHandle().isOwner(); 359 } 360 361 public static void cleanFotaApnResources(Context context) { 362 if (DBG) logd("Inside cleanFotaApnResources"); 363 clearFotaApnSharedPreferences(context); 364 } 365 366 public static boolean disableIfSecondaryUser(Context context) { 367 if (sfirstTriggerReceived == false) { 368 sfirstTriggerReceived = true; 369 if (!isRunningAsOwner()) { 370 PackageManager pm = context.getPackageManager(); 371 logd("Disabling com.android.omadm.service for secondary user"); 372 pm.setApplicationEnabledSetting("com.android.omadm.service", 373 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0 ); 374 return true; 375 } 376 } 377 return false; 378 } 379 380 private static void logd(String msg) { 381 Log.d(TAG, msg); 382 } 383 } 384