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