17903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng/* 27903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * Copyright (C) 2010 The Android Open Source Project 37903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * 47903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * Licensed under the Apache License, Version 2.0 (the "License"); 57903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * you may not use this file except in compliance with the License. 67903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * You may obtain a copy of the License at 77903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * 87903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * http://www.apache.org/licenses/LICENSE-2.0 97903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * 107903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * Unless required by applicable law or agreed to in writing, software 117903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * distributed under the License is distributed on an "AS IS" BASIS, 127903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * See the License for the specific language governing permissions and 147903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * limitations under the License. 157903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng */ 167903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengpackage com.android.contacts.common.vcard; 177903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 187903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport android.app.Service; 197903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport android.content.Intent; 207903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport android.content.res.Resources; 217903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport android.media.MediaScannerConnection; 227903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport android.media.MediaScannerConnection.MediaScannerConnectionClient; 237903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport android.net.Uri; 247903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport android.os.Binder; 257903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport android.os.Environment; 267903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport android.os.IBinder; 277903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport android.os.Message; 287903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport android.os.Messenger; 297903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport android.os.RemoteException; 307903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport android.text.TextUtils; 317903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport android.util.Log; 327903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport android.util.SparseArray; 337903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 347903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport com.android.contacts.common.R; 357903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 367903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport java.io.File; 377903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport java.util.ArrayList; 387903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport java.util.HashSet; 397903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport java.util.List; 407903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport java.util.Set; 417903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport java.util.concurrent.ExecutorService; 427903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport java.util.concurrent.Executors; 437903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengimport java.util.concurrent.RejectedExecutionException; 447903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 457903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng/** 467903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * The class responsible for handling vCard import/export requests. 477903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * 487903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * This Service creates one ImportRequest/ExportRequest object (as Runnable) per request and push 497903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * it to {@link ExecutorService} with single thread executor. The executor handles each request 507903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * one by one, and notifies users when needed. 517903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng */ 527903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng// TODO: Using IntentService looks simpler than using Service + ServiceConnection though this 537903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng// works fine enough. Investigate the feasibility. 547903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Chengpublic class VCardService extends Service { 557903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private final static String LOG_TAG = "VCardService"; 567903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 577903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /* package */ final static boolean DEBUG = false; 587903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 597903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /* package */ static final int MSG_IMPORT_REQUEST = 1; 607903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /* package */ static final int MSG_EXPORT_REQUEST = 2; 617903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /* package */ static final int MSG_CANCEL_REQUEST = 3; 627903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /* package */ static final int MSG_REQUEST_AVAILABLE_EXPORT_DESTINATION = 4; 637903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /* package */ static final int MSG_SET_AVAILABLE_EXPORT_DESTINATION = 5; 647903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 657903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /** 667903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * Specifies the type of operation. Used when constructing a notification, canceling 677903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * some operation, etc. 687903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng */ 697903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /* package */ static final int TYPE_IMPORT = 1; 707903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /* package */ static final int TYPE_EXPORT = 2; 717903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 727903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /* package */ static final String CACHE_FILE_PREFIX = "import_tmp_"; 737903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 747903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 757903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private class CustomMediaScannerConnectionClient implements MediaScannerConnectionClient { 767903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final MediaScannerConnection mConnection; 777903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final String mPath; 787903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 797903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng public CustomMediaScannerConnectionClient(String path) { 807903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mConnection = new MediaScannerConnection(VCardService.this, this); 817903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mPath = path; 827903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 837903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 847903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng public void start() { 857903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mConnection.connect(); 867903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 877903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 887903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng @Override 897903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng public void onMediaScannerConnected() { 907903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (DEBUG) { Log.d(LOG_TAG, "Connected to MediaScanner. Start scanning."); } 917903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mConnection.scanFile(mPath, null); 927903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 937903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 947903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng @Override 957903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng public void onScanCompleted(String path, Uri uri) { 967903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (DEBUG) { Log.d(LOG_TAG, "scan completed: " + path); } 977903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mConnection.disconnect(); 987903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng removeConnectionClient(this); 997903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 1007903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 1017903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 1027903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // Should be single thread, as we don't want to simultaneously handle import and export 1037903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // requests. 1047903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor(); 1057903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 1067903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private int mCurrentJobId; 1077903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 1087903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // Stores all unfinished import/export jobs which will be executed by mExecutorService. 1097903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // Key is jobId. 1107903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private final SparseArray<ProcessorBase> mRunningJobMap = new SparseArray<ProcessorBase>(); 1117903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // Stores ScannerConnectionClient objects until they finish scanning requested files. 1127903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // Uses List class for simplicity. It's not costly as we won't have multiple objects in 1137903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // almost all cases. 1147903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private final List<CustomMediaScannerConnectionClient> mRemainingScannerConnections = 1157903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng new ArrayList<CustomMediaScannerConnectionClient>(); 1167903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 1177903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /* ** vCard exporter params ** */ 1187903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // If true, VCardExporter is able to emits files longer than 8.3 format. 1197903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private static final boolean ALLOW_LONG_FILE_NAME = false; 1207903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 1217903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private File mTargetDirectory; 1227903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private String mFileNamePrefix; 1237903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private String mFileNameSuffix; 1247903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private int mFileIndexMinimum; 1257903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private int mFileIndexMaximum; 1267903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private String mFileNameExtension; 1277903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private Set<String> mExtensionsToConsider; 1287903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private String mErrorReason; 1297903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private MyBinder mBinder; 1307903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 1317903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private String mCallingActivity; 1327903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 1337903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // File names currently reserved by some export job. 1347903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private final Set<String> mReservedDestination = new HashSet<String>(); 1357903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /* ** end of vCard exporter params ** */ 1367903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 1377903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng public class MyBinder extends Binder { 1387903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng public VCardService getService() { 1397903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng return VCardService.this; 1407903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 1417903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 1427903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 1437903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng @Override 1447903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng public void onCreate() { 1457903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng super.onCreate(); 1467903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mBinder = new MyBinder(); 1477903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (DEBUG) Log.d(LOG_TAG, "vCard Service is being created."); 1487903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng initExporterParams(); 1497903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 1507903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 1517903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private void initExporterParams() { 1527903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mTargetDirectory = Environment.getExternalStorageDirectory(); 1537903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mFileNamePrefix = getString(R.string.config_export_file_prefix); 1547903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mFileNameSuffix = getString(R.string.config_export_file_suffix); 1557903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mFileNameExtension = getString(R.string.config_export_file_extension); 1567903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 1577903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mExtensionsToConsider = new HashSet<String>(); 1587903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mExtensionsToConsider.add(mFileNameExtension); 1597903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 1607903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final String additionalExtensions = 1617903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng getString(R.string.config_export_extensions_to_consider); 1627903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (!TextUtils.isEmpty(additionalExtensions)) { 1637903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng for (String extension : additionalExtensions.split(",")) { 1647903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng String trimed = extension.trim(); 1657903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (trimed.length() > 0) { 1667903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mExtensionsToConsider.add(trimed); 1677903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 1687903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 1697903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 1707903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 1717903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final Resources resources = getResources(); 1727903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mFileIndexMinimum = resources.getInteger(R.integer.config_export_file_min_index); 1737903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mFileIndexMaximum = resources.getInteger(R.integer.config_export_file_max_index); 1747903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 1757903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 1767903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng @Override 1777903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng public int onStartCommand(Intent intent, int flags, int id) { 178dccb6cfd65b8d120a024bde6b0e31acaf58e743aYorke Lee if (intent != null && intent.getExtras() != null) { 179dccb6cfd65b8d120a024bde6b0e31acaf58e743aYorke Lee mCallingActivity = intent.getExtras().getString( 180dccb6cfd65b8d120a024bde6b0e31acaf58e743aYorke Lee VCardCommonArguments.ARG_CALLING_ACTIVITY); 181dccb6cfd65b8d120a024bde6b0e31acaf58e743aYorke Lee } else { 182dccb6cfd65b8d120a024bde6b0e31acaf58e743aYorke Lee mCallingActivity = null; 183dccb6cfd65b8d120a024bde6b0e31acaf58e743aYorke Lee } 1847903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng return START_STICKY; 1857903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 1867903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 1877903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng @Override 1887903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng public IBinder onBind(Intent intent) { 1897903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng return mBinder; 1907903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 1917903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 1927903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng @Override 1937903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng public void onDestroy() { 1947903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (DEBUG) Log.d(LOG_TAG, "VCardService is being destroyed."); 1957903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng cancelAllRequestsAndShutdown(); 1967903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng clearCache(); 1977903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng super.onDestroy(); 1987903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 1997903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 2007903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng public synchronized void handleImportRequest(List<ImportRequest> requests, 2017903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng VCardImportExportListener listener) { 2027903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (DEBUG) { 2037903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final ArrayList<String> uris = new ArrayList<String>(); 2047903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final ArrayList<String> displayNames = new ArrayList<String>(); 2057903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng for (ImportRequest request : requests) { 2067903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng uris.add(request.uri.toString()); 2077903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng displayNames.add(request.displayName); 2087903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2097903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.d(LOG_TAG, 2107903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng String.format("received multiple import request (uri: %s, displayName: %s)", 2117903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng uris.toString(), displayNames.toString())); 2127903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2137903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final int size = requests.size(); 2147903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng for (int i = 0; i < size; i++) { 2157903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng ImportRequest request = requests.get(i); 2167903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 2177903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (tryExecute(new ImportProcessor(this, listener, request, mCurrentJobId))) { 2187903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (listener != null) { 2197903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng listener.onImportProcessed(request, mCurrentJobId, i); 2207903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2217903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mCurrentJobId++; 2227903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } else { 2237903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (listener != null) { 2247903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng listener.onImportFailed(request); 2257903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2267903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // A rejection means executor doesn't run any more. Exit. 2277903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng break; 2287903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2297903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2307903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2317903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 2327903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng public synchronized void handleExportRequest(ExportRequest request, 2337903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng VCardImportExportListener listener) { 2347903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (tryExecute(new ExportProcessor(this, request, mCurrentJobId, mCallingActivity))) { 2357903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final String path = request.destUri.getEncodedPath(); 2367903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (DEBUG) Log.d(LOG_TAG, "Reserve the path " + path); 2377903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (!mReservedDestination.add(path)) { 2387903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.w(LOG_TAG, 2397903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng String.format("The path %s is already reserved. Reject export request", 2407903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng path)); 2417903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (listener != null) { 2427903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng listener.onExportFailed(request); 2437903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2447903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng return; 2457903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2467903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 2477903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (listener != null) { 2487903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng listener.onExportProcessed(request, mCurrentJobId); 2497903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2507903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mCurrentJobId++; 2517903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } else { 2527903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (listener != null) { 2537903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng listener.onExportFailed(request); 2547903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2557903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2567903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2577903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 2587903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /** 2597903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * Tries to call {@link ExecutorService#execute(Runnable)} toward a given processor. 2607903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * @return true when successful. 2617903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng */ 2627903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private synchronized boolean tryExecute(ProcessorBase processor) { 2637903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng try { 2647903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (DEBUG) { 2657903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.d(LOG_TAG, "Executor service status: shutdown: " + mExecutorService.isShutdown() 2667903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng + ", terminated: " + mExecutorService.isTerminated()); 2677903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2687903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mExecutorService.execute(processor); 2697903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mRunningJobMap.put(mCurrentJobId, processor); 2707903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng return true; 2717903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } catch (RejectedExecutionException e) { 2727903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.w(LOG_TAG, "Failed to excetute a job.", e); 2737903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng return false; 2747903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2757903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2767903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 2777903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng public synchronized void handleCancelRequest(CancelRequest request, 2787903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng VCardImportExportListener listener) { 2797903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final int jobId = request.jobId; 2807903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (DEBUG) Log.d(LOG_TAG, String.format("Received cancel request. (id: %d)", jobId)); 2817903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 2827903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final ProcessorBase processor = mRunningJobMap.get(jobId); 2837903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mRunningJobMap.remove(jobId); 2847903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 2857903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (processor != null) { 2867903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng processor.cancel(true); 2877903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final int type = processor.getType(); 2887903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (listener != null) { 2897903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng listener.onCancelRequest(request, type); 2907903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2917903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (type == TYPE_EXPORT) { 2927903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final String path = 2937903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng ((ExportProcessor)processor).getRequest().destUri.getEncodedPath(); 2947903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.i(LOG_TAG, 2957903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng String.format("Cancel reservation for the path %s if appropriate", path)); 2967903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (!mReservedDestination.remove(path)) { 2977903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.w(LOG_TAG, "Not reserved."); 2987903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 2997903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3007903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } else { 3017903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.w(LOG_TAG, String.format("Tried to remove unknown job (id: %d)", jobId)); 3027903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3037903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng stopServiceIfAppropriate(); 3047903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3057903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 3067903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng public synchronized void handleRequestAvailableExportDestination(final Messenger messenger) { 3077903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (DEBUG) Log.d(LOG_TAG, "Received available export destination request."); 3087903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final String path = getAppropriateDestination(mTargetDirectory); 3097903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final Message message; 3107903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (path != null) { 3117903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng message = Message.obtain(null, 3127903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng VCardService.MSG_SET_AVAILABLE_EXPORT_DESTINATION, 0, 0, path); 3137903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } else { 3147903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng message = Message.obtain(null, 3157903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng VCardService.MSG_SET_AVAILABLE_EXPORT_DESTINATION, 3167903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng R.id.dialog_fail_to_export_with_reason, 0, mErrorReason); 3177903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3187903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng try { 3197903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng messenger.send(message); 3207903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } catch (RemoteException e) { 3217903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.w(LOG_TAG, "Failed to send reply for available export destination request.", e); 3227903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3237903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3247903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 3257903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /** 3267903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * Checks job list and call {@link #stopSelf()} when there's no job and no scanner connection 3277903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * is remaining. 3287903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * A new job (import/export) cannot be submitted any more after this call. 3297903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng */ 3307903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private synchronized void stopServiceIfAppropriate() { 3317903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (mRunningJobMap.size() > 0) { 3327903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final int size = mRunningJobMap.size(); 3337903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 3347903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // Check if there are processors which aren't finished yet. If we still have ones to 3357903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // process, we cannot stop the service yet. Also clean up already finished processors 3367903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // here. 3377903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 3387903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // Job-ids to be removed. At first all elements in the array are invalid and will 3397903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // be filled with real job-ids from the array's top. When we find a not-yet-finished 3407903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // processor, then we start removing those finished jobs. In that case latter half of 3417903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // this array will be invalid. 3427903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final int[] toBeRemoved = new int[size]; 3437903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng for (int i = 0; i < size; i++) { 3447903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final int jobId = mRunningJobMap.keyAt(i); 3457903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final ProcessorBase processor = mRunningJobMap.valueAt(i); 3467903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (!processor.isDone()) { 3477903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.i(LOG_TAG, String.format("Found unfinished job (id: %d)", jobId)); 3487903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 3497903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // Remove processors which are already "done", all of which should be before 3507903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // processors which aren't done yet. 3517903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng for (int j = 0; j < i; j++) { 3527903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mRunningJobMap.remove(toBeRemoved[j]); 3537903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3547903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng return; 3557903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3567903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 3577903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // Remember the finished processor. 3587903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng toBeRemoved[i] = jobId; 3597903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3607903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 3617903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // We're sure we can remove all. Instead of removing one by one, just call clear(). 3627903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mRunningJobMap.clear(); 3637903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3647903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 3657903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (!mRemainingScannerConnections.isEmpty()) { 3667903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.i(LOG_TAG, "MediaScanner update is in progress."); 3677903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng return; 3687903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3697903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 3707903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.i(LOG_TAG, "No unfinished job. Stop this service."); 3717903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mExecutorService.shutdown(); 3727903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng stopSelf(); 3737903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3747903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 3757903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /* package */ synchronized void updateMediaScanner(String path) { 3767903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (DEBUG) { 3777903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.d(LOG_TAG, "MediaScanner is being updated: " + path); 3787903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3797903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 3807903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (mExecutorService.isShutdown()) { 3817903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.w(LOG_TAG, "MediaScanner update is requested after executor's being shut down. " + 3827903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng "Ignoring the update request"); 3837903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng return; 3847903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3857903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final CustomMediaScannerConnectionClient client = 3867903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng new CustomMediaScannerConnectionClient(path); 3877903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mRemainingScannerConnections.add(client); 3887903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng client.start(); 3897903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3907903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 3917903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private synchronized void removeConnectionClient( 3927903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng CustomMediaScannerConnectionClient client) { 3937903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (DEBUG) { 3947903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.d(LOG_TAG, "Removing custom MediaScannerConnectionClient."); 3957903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3967903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mRemainingScannerConnections.remove(client); 3977903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng stopServiceIfAppropriate(); 3987903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 3997903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 4007903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /* package */ synchronized void handleFinishImportNotification( 4017903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng int jobId, boolean successful) { 4027903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (DEBUG) { 4037903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.d(LOG_TAG, String.format("Received vCard import finish notification (id: %d). " 4047903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng + "Result: %b", jobId, (successful ? "success" : "failure"))); 4057903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 4067903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mRunningJobMap.remove(jobId); 4077903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng stopServiceIfAppropriate(); 4087903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 4097903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 4107903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /* package */ synchronized void handleFinishExportNotification( 4117903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng int jobId, boolean successful) { 4127903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (DEBUG) { 4137903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.d(LOG_TAG, String.format("Received vCard export finish notification (id: %d). " 4147903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng + "Result: %b", jobId, (successful ? "success" : "failure"))); 4157903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 4167903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final ProcessorBase job = mRunningJobMap.get(jobId); 4177903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mRunningJobMap.remove(jobId); 4187903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (job == null) { 4197903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.w(LOG_TAG, String.format("Tried to remove unknown job (id: %d)", jobId)); 4207903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } else if (!(job instanceof ExportProcessor)) { 4217903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.w(LOG_TAG, 4227903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng String.format("Removed job (id: %s) isn't ExportProcessor", jobId)); 4237903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } else { 4247903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final String path = ((ExportProcessor)job).getRequest().destUri.getEncodedPath(); 4257903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (DEBUG) Log.d(LOG_TAG, "Remove reserved path " + path); 4267903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mReservedDestination.remove(path); 4277903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 4287903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 4297903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng stopServiceIfAppropriate(); 4307903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 4317903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 4327903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /** 4337903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * Cancels all the import/export requests and calls {@link ExecutorService#shutdown()}, which 4347903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * means this Service becomes no longer ready for import/export requests. 4357903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * 4367903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * Mainly called from onDestroy(). 4377903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng */ 4387903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private synchronized void cancelAllRequestsAndShutdown() { 4397903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng for (int i = 0; i < mRunningJobMap.size(); i++) { 4407903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mRunningJobMap.valueAt(i).cancel(true); 4417903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 4427903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mRunningJobMap.clear(); 4437903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mExecutorService.shutdown(); 4447903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 4457903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 4467903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /** 4477903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * Removes import caches stored locally. 4487903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng */ 4497903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private void clearCache() { 4507903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng for (final String fileName : fileList()) { 4517903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (fileName.startsWith(CACHE_FILE_PREFIX)) { 4527903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // We don't want to keep all the caches so we remove cache files old enough. 4537903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.i(LOG_TAG, "Remove a temporary file: " + fileName); 4547903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng deleteFile(fileName); 4557903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 4567903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 4577903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 4587903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 4597903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /** 4607903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * Returns an appropriate file name for vCard export. Returns null when impossible. 4617903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * 4627903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * @return destination path for a vCard file to be exported. null on error and mErrorReason 4637903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * is correctly set. 4647903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng */ 4657903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng private String getAppropriateDestination(final File destDirectory) { 4667903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng /* 4677903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * Here, file names have 5 parts: directory, prefix, index, suffix, and extension. 4687903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * e.g. "/mnt/sdcard/prfx00001sfx.vcf" -> "/mnt/sdcard", "prfx", "00001", "sfx", and ".vcf" 4697903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * (In default, prefix and suffix is empty, so usually the destination would be 4707903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * /mnt/sdcard/00001.vcf.) 4717903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * 4727903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * This method increments "index" part from 1 to maximum, and checks whether any file name 4737903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * following naming rule is available. If there's no file named /mnt/sdcard/00001.vcf, the 4747903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * name will be returned to a caller. If there are 00001.vcf 00002.vcf, 00003.vcf is 4757903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * returned. 4767903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * 4777903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * There may not be any appropriate file name. If there are 99999 vCard files in the 4787903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * storage, for example, there's no appropriate name, so this method returns 4797903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng * null. 4807903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng */ 4817903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 4827903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // Count the number of digits of mFileIndexMaximum 4837903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // e.g. When mFileIndexMaximum is 99999, fileIndexDigit becomes 5, as we will count the 4847903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng int fileIndexDigit = 0; 4857903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng { 4867903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // Calling Math.Log10() is costly. 4877903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng int tmp; 4887903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng for (fileIndexDigit = 0, tmp = mFileIndexMaximum; tmp > 0; 4897903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng fileIndexDigit++, tmp /= 10) { 4907903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 4917903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 4927903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 4937903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // %s05d%s (e.g. "p00001s") 4947903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final String bodyFormat = "%s%0" + fileIndexDigit + "d%s"; 4957903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 4967903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (!ALLOW_LONG_FILE_NAME) { 4977903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final String possibleBody = 4987903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng String.format(bodyFormat, mFileNamePrefix, 1, mFileNameSuffix); 4997903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (possibleBody.length() > 8 || mFileNameExtension.length() > 3) { 5007903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.e(LOG_TAG, "This code does not allow any long file name."); 5017903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mErrorReason = getString(R.string.fail_reason_too_long_filename, 5027903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng String.format("%s.%s", possibleBody, mFileNameExtension)); 5037903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.w(LOG_TAG, "File name becomes too long."); 5047903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng return null; 5057903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 5067903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 5077903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 5087903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng for (int i = mFileIndexMinimum; i <= mFileIndexMaximum; i++) { 5097903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng boolean numberIsAvailable = true; 5107903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final String body = String.format(bodyFormat, mFileNamePrefix, i, mFileNameSuffix); 5117903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // Make sure that none of the extensions of mExtensionsToConsider matches. If this 5127903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // number is free, we'll go ahead with mFileNameExtension (which is included in 5137903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // mExtensionsToConsider) 5147903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng for (String possibleExtension : mExtensionsToConsider) { 5157903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final File file = new File(destDirectory, body + "." + possibleExtension); 5167903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng final String path = file.getAbsolutePath(); 5177903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng synchronized (this) { 5187903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng // Is this being exported right now? Skip this number 5197903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (mReservedDestination.contains(path)) { 5207903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (DEBUG) { 5217903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.d(LOG_TAG, String.format("%s is already being exported.", path)); 5227903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 5237903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng numberIsAvailable = false; 5247903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng break; 5257903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 5267903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 5277903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (file.exists()) { 5287903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng numberIsAvailable = false; 5297903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng break; 5307903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 5317903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 5327903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng if (numberIsAvailable) { 5337903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng return new File(destDirectory, body + "." + mFileNameExtension).getAbsolutePath(); 5347903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 5357903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 5367903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng 5377903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng Log.w(LOG_TAG, "Reached vCard number limit. Maybe there are too many vCard in the storage"); 5387903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng mErrorReason = getString(R.string.fail_reason_too_many_vcard); 5397903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng return null; 5407903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng } 5417903d2473e1120e32fa5380a7d7532d0a21e2180Chiao Cheng} 542