Revert "Initial checkin of AOSP Messaging app."
This reverts commit 461a34b466.
Change-Id: Iac4ca77eeaa94989e91dead49a7959c905bd3078
This commit is contained in:
@@ -1,465 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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 android.support.v7.mms;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.os.Process;
|
||||
import android.telephony.SmsManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
/**
|
||||
* Service to execute MMS requests using deprecated legacy APIs on older platform (prior to L)
|
||||
*/
|
||||
public class MmsService extends Service {
|
||||
static final String TAG = "MmsLib";
|
||||
|
||||
//The default number of threads allowed to run MMS requests
|
||||
private static final int DEFAULT_THREAD_POOL_SIZE = 4;
|
||||
// Delay before stopping the service
|
||||
private static final int SERVICE_STOP_DELAY_MILLIS = 2000;
|
||||
|
||||
private static final String EXTRA_REQUEST = "request";
|
||||
private static final String EXTRA_MYPID = "mypid";
|
||||
|
||||
private static final String WAKELOCK_ID = "mmslib_wakelock";
|
||||
|
||||
/**
|
||||
* Thread pool size for each request queue
|
||||
*/
|
||||
private static volatile int sThreadPoolSize = DEFAULT_THREAD_POOL_SIZE;
|
||||
|
||||
/**
|
||||
* Optional wake lock to use
|
||||
*/
|
||||
private static volatile boolean sUseWakeLock = true;
|
||||
private static volatile PowerManager.WakeLock sWakeLock = null;
|
||||
private static final Object sWakeLockLock = new Object();
|
||||
|
||||
/**
|
||||
* Carrier configuration values loader
|
||||
*/
|
||||
private static volatile CarrierConfigValuesLoader sCarrierConfigValuesLoader = null;
|
||||
|
||||
/**
|
||||
* APN loader
|
||||
*/
|
||||
private static volatile ApnSettingsLoader sApnSettingsLoader = null;
|
||||
|
||||
/**
|
||||
* UserAgent and UA Prof URL loader
|
||||
*/
|
||||
private static volatile UserAgentInfoLoader sUserAgentInfoLoader = null;
|
||||
|
||||
/**
|
||||
* Set the size of thread pool for request execution.
|
||||
* Default is DEFAULT_THREAD_POOL_SIZE
|
||||
*
|
||||
* @param size thread pool size
|
||||
*/
|
||||
static void setThreadPoolSize(final int size) {
|
||||
sThreadPoolSize = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to use wake lock
|
||||
*
|
||||
* @param useWakeLock true to use wake lock, false otherwise
|
||||
*/
|
||||
static void setUseWakeLock(final boolean useWakeLock) {
|
||||
sUseWakeLock = useWakeLock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the optional carrier config values
|
||||
*
|
||||
* @param loader the carrier config values loader
|
||||
*/
|
||||
static void setCarrierConfigValuesLoader(final CarrierConfigValuesLoader loader) {
|
||||
sCarrierConfigValuesLoader = loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current carrier config values loader
|
||||
*
|
||||
* @return the carrier config values loader currently set
|
||||
*/
|
||||
static CarrierConfigValuesLoader getCarrierConfigValuesLoader() {
|
||||
return sCarrierConfigValuesLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set APN settings loader
|
||||
*
|
||||
* @param loader the APN settings loader
|
||||
*/
|
||||
static void setApnSettingsLoader(final ApnSettingsLoader loader) {
|
||||
sApnSettingsLoader = loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current APN settings loader
|
||||
*
|
||||
* @return the APN settings loader currently set
|
||||
*/
|
||||
static ApnSettingsLoader getApnSettingsLoader() {
|
||||
return sApnSettingsLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user agent info loader
|
||||
*
|
||||
* @param loader the user agent info loader
|
||||
*/
|
||||
static void setUserAgentInfoLoader(final UserAgentInfoLoader loader) {
|
||||
sUserAgentInfoLoader = loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current user agent info loader
|
||||
*
|
||||
* @return the user agent info loader currently set
|
||||
*/
|
||||
static UserAgentInfoLoader getUserAgentInfoLoader() {
|
||||
return sUserAgentInfoLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure loaders are not null. Set to default if that's the case
|
||||
*
|
||||
* @param context the Context to use
|
||||
*/
|
||||
private static void ensureLoaders(final Context context) {
|
||||
if (sUserAgentInfoLoader == null) {
|
||||
sUserAgentInfoLoader = new DefaultUserAgentInfoLoader(context);
|
||||
}
|
||||
if (sCarrierConfigValuesLoader == null) {
|
||||
sCarrierConfigValuesLoader = new DefaultCarrierConfigValuesLoader(context);
|
||||
}
|
||||
if (sApnSettingsLoader == null) {
|
||||
sApnSettingsLoader = new DefaultApnSettingsLoader(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire the wake lock
|
||||
*
|
||||
* @param context the context to use
|
||||
*/
|
||||
private static void acquireWakeLock(final Context context) {
|
||||
synchronized (sWakeLockLock) {
|
||||
if (sWakeLock == null) {
|
||||
final PowerManager pm =
|
||||
(PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
sWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_ID);
|
||||
}
|
||||
sWakeLock.acquire();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the wake lock
|
||||
*/
|
||||
private static void releaseWakeLock() {
|
||||
boolean releasedEmptyWakeLock = false;
|
||||
synchronized (sWakeLockLock) {
|
||||
if (sWakeLock != null) {
|
||||
sWakeLock.release();
|
||||
} else {
|
||||
releasedEmptyWakeLock = true;
|
||||
}
|
||||
}
|
||||
if (releasedEmptyWakeLock) {
|
||||
Log.w(TAG, "Releasing empty wake lock");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if wake lock is not held (e.g. when service stops)
|
||||
*/
|
||||
private static void verifyWakeLockNotHeld() {
|
||||
boolean wakeLockHeld = false;
|
||||
synchronized (sWakeLockLock) {
|
||||
wakeLockHeld = sWakeLock != null && sWakeLock.isHeld();
|
||||
}
|
||||
if (wakeLockHeld) {
|
||||
Log.e(TAG, "Wake lock still held!");
|
||||
}
|
||||
}
|
||||
|
||||
// Remember my PID to discard restarted intent
|
||||
private static volatile int sMyPid = -1;
|
||||
|
||||
/**
|
||||
* Get the current PID
|
||||
*
|
||||
* @return the current PID
|
||||
*/
|
||||
private static int getMyPid() {
|
||||
if (sMyPid < 0) {
|
||||
sMyPid = Process.myPid();
|
||||
}
|
||||
return sMyPid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the intent is coming from this process
|
||||
*
|
||||
* @param intent the incoming intent for the service
|
||||
* @return true if the intent is from the current process
|
||||
*/
|
||||
private static boolean fromThisProcess(final Intent intent) {
|
||||
final int pid = intent.getIntExtra(EXTRA_MYPID, -1);
|
||||
return pid == getMyPid();
|
||||
}
|
||||
|
||||
// Request execution thread pools. One thread pool for sending and one for downloading.
|
||||
// The size of the thread pool controls the parallelism of request execution.
|
||||
// See {@link setThreadPoolSize}
|
||||
private ExecutorService[] mExecutors = new ExecutorService[2];
|
||||
|
||||
// Active request count
|
||||
private int mActiveRequestCount;
|
||||
// The latest intent startId, used for safely stopping service
|
||||
private int mLastStartId;
|
||||
|
||||
private MmsNetworkManager mNetworkManager;
|
||||
|
||||
// Handler for scheduling service stop
|
||||
private final Handler mHandler = new Handler();
|
||||
// Service stop task
|
||||
private final Runnable mServiceStopRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
tryStopService();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Start the service with a request
|
||||
*
|
||||
* @param context the Context to use
|
||||
* @param request the request to start
|
||||
*/
|
||||
public static void startRequest(final Context context, final MmsRequest request) {
|
||||
final boolean useWakeLock = sUseWakeLock;
|
||||
request.setUseWakeLock(useWakeLock);
|
||||
final Intent intent = new Intent(context, MmsService.class);
|
||||
intent.putExtra(EXTRA_REQUEST, request);
|
||||
intent.putExtra(EXTRA_MYPID, getMyPid());
|
||||
if (useWakeLock) {
|
||||
acquireWakeLock(context);
|
||||
}
|
||||
if (context.startService(intent) == null) {
|
||||
if (useWakeLock) {
|
||||
releaseWakeLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
ensureLoaders(this);
|
||||
|
||||
for (int i = 0; i < mExecutors.length; i++) {
|
||||
mExecutors[i] = Executors.newFixedThreadPool(sThreadPoolSize);
|
||||
}
|
||||
|
||||
mNetworkManager = new MmsNetworkManager(this);
|
||||
|
||||
synchronized (this) {
|
||||
mActiveRequestCount = 0;
|
||||
mLastStartId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
for (ExecutorService executor : mExecutors) {
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
// Always remember the latest startId for use when we try releasing the service
|
||||
synchronized (this) {
|
||||
mLastStartId = startId;
|
||||
}
|
||||
boolean scheduled = false;
|
||||
if (intent != null) {
|
||||
// There is a rare situation that right after a intent is started,
|
||||
// the service gets killed. Then the service will restart with
|
||||
// the old intent which we don't want it to run since it will
|
||||
// break our assumption for wake lock. Check the process ID
|
||||
// embedded in the intent to make sure it is indeed from the
|
||||
// the current life of this service.
|
||||
if (fromThisProcess(intent)) {
|
||||
final MmsRequest request = intent.getParcelableExtra(EXTRA_REQUEST);
|
||||
if (request != null) {
|
||||
try {
|
||||
retainService(request, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
request.execute(
|
||||
MmsService.this,
|
||||
mNetworkManager,
|
||||
getApnSettingsLoader(),
|
||||
getCarrierConfigValuesLoader(),
|
||||
getUserAgentInfoLoader());
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Unexpected execution failure", e);
|
||||
} finally {
|
||||
if (request.getUseWakeLock()) {
|
||||
releaseWakeLock();
|
||||
}
|
||||
releaseService();
|
||||
}
|
||||
}
|
||||
});
|
||||
scheduled = true;
|
||||
} catch (RejectedExecutionException e) {
|
||||
// Rare thing happened. Send back failure using the pending intent
|
||||
// and also release the wake lock.
|
||||
Log.w(TAG, "Executing request failed " + e);
|
||||
request.returnResult(this, SmsManager.MMS_ERROR_UNSPECIFIED,
|
||||
null/*response*/, 0/*httpStatusCode*/);
|
||||
if (request.getUseWakeLock()) {
|
||||
releaseWakeLock();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Empty request");
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Got a restarted intent from previous incarnation");
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Empty intent");
|
||||
}
|
||||
if (!scheduled) {
|
||||
// If the request is not started successfully, we need to try shutdown the service
|
||||
// if nobody is using it.
|
||||
tryScheduleStop();
|
||||
}
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retain the service for executing the request in service thread pool
|
||||
*
|
||||
* @param request The request to execute
|
||||
* @param runnable The runnable to run the request in thread pool
|
||||
*/
|
||||
private void retainService(final MmsRequest request, final Runnable runnable) {
|
||||
final ExecutorService executor = getRequestExecutor(request);
|
||||
synchronized (this) {
|
||||
executor.execute(runnable);
|
||||
mActiveRequestCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the service from the request. If nobody is using it, schedule service stop.
|
||||
*/
|
||||
private void releaseService() {
|
||||
synchronized (this) {
|
||||
mActiveRequestCount--;
|
||||
if (mActiveRequestCount <= 0) {
|
||||
mActiveRequestCount = 0;
|
||||
rescheduleServiceStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the service stop if there is no active request
|
||||
*/
|
||||
private void tryScheduleStop() {
|
||||
synchronized (this) {
|
||||
if (mActiveRequestCount == 0) {
|
||||
rescheduleServiceStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reschedule service stop task
|
||||
*/
|
||||
private void rescheduleServiceStop() {
|
||||
mHandler.removeCallbacks(mServiceStopRunnable);
|
||||
mHandler.postDelayed(mServiceStopRunnable, SERVICE_STOP_DELAY_MILLIS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Really try to stop the service if there is not active request
|
||||
*/
|
||||
private void tryStopService() {
|
||||
Boolean stopped = null;
|
||||
synchronized (this) {
|
||||
if (mActiveRequestCount == 0) {
|
||||
stopped = stopSelfResult(mLastStartId);
|
||||
}
|
||||
}
|
||||
logServiceStop(stopped);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the result of service stopping. Also check wake lock status when service stops.
|
||||
*
|
||||
* @param stopped Not empty if service stop is performed: true if really stopped, false
|
||||
* if cancelled.
|
||||
*/
|
||||
private void logServiceStop(final Boolean stopped) {
|
||||
if (stopped != null) {
|
||||
if (stopped) {
|
||||
Log.i(TAG, "Service successfully stopped");
|
||||
verifyWakeLockNotHeld();
|
||||
} else {
|
||||
Log.i(TAG, "Service stopping cancelled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ExecutorService getRequestExecutor(final MmsRequest request) {
|
||||
if (request instanceof SendRequest) {
|
||||
// Send
|
||||
return mExecutors[0];
|
||||
} else {
|
||||
// Download
|
||||
return mExecutors[1];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user