VCardService.java revision 7c819a1a434e02c54f6d216aa3b1a0d08cc93f50
1f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa/*
2f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa * Copyright (C) 2010 The Android Open Source Project
3f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa *
4f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License");
5f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa * you may not use this file except in compliance with the License.
6f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa * You may obtain a copy of the License at
7f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa *
8f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa *      http://www.apache.org/licenses/LICENSE-2.0
9f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa *
10f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa * Unless required by applicable law or agreed to in writing, software
11f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS,
12f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa * See the License for the specific language governing permissions and
14f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa * limitations under the License.
15f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa */
161b918e58f4a3ae8d32af83f6f69bbf2de57a94f9Daisuke Miyakawapackage com.android.contacts.vcard;
17f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa
187c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawaimport com.android.contacts.R;
197c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
20f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawaimport android.app.Service;
21f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawaimport android.content.Intent;
22476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawaimport android.os.Handler;
23f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawaimport android.os.IBinder;
24476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawaimport android.os.Message;
25476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawaimport android.os.Messenger;
26f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawaimport android.util.Log;
27f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawaimport android.widget.Toast;
28f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa
29aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawaimport java.io.File;
30aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawaimport java.util.Date;
317c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawaimport java.util.HashMap;
327c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawaimport java.util.Map;
337c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawaimport java.util.concurrent.ExecutorService;
347c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawaimport java.util.concurrent.Executors;
357c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawaimport java.util.concurrent.RejectedExecutionException;
361b918e58f4a3ae8d32af83f6f69bbf2de57a94f9Daisuke Miyakawa
37f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa/**
387c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa * The class responsible for handling vCard import/export requests.
397c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa *
407c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa * This Service creates one ImportRequest/ExportRequest object (as Runnable) per request and push
417c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa * it to {@link ExecutorService} with single thread executor. The executor handles each request
427c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa * one by one, and notifies users when needed.
43f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa */
447c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa// TODO: Using IntentService looks simpler than using Service + ServiceConnection though this
457c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa// works fine enough. Investigate the feasibility.
46d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawapublic class VCardService extends Service {
477c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private final static String LOG_TAG = "VCardService";
48f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa
49ef41f8866e8e7d52e04907f7282adcf5f4749f25Daisuke Miyakawa    /* package */ static final int MSG_IMPORT_REQUEST = 1;
50d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawa    /* package */ static final int MSG_EXPORT_REQUEST = 2;
5118b5190d6ed37be04d153a5d6f205076b38ac479Daisuke Miyakawa    /* package */ static final int MSG_CANCEL_IMPORT_REQUEST = 3;
52f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa
53d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawa    /* package */ static final int IMPORT_NOTIFICATION_ID = 1000;
54d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawa    /* package */ static final int EXPORT_NOTIFICATION_ID = 1001;
55f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa
56aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa    /* package */ static final String CACHE_FILE_PREFIX = "import_tmp_";
57f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa
587c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    public class RequestHandler extends Handler {
59476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa        @Override
60476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa        public void handleMessage(Message msg) {
61476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa            switch (msg.what) {
62ef41f8866e8e7d52e04907f7282adcf5f4749f25Daisuke Miyakawa                case MSG_IMPORT_REQUEST: {
637c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                    handleImportRequest((ImportRequest)msg.obj);
64476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa                    break;
65915723665339c73c9bdebc7bf8ef56c414602c2cDaisuke Miyakawa                }
66d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawa                case MSG_EXPORT_REQUEST: {
677c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                    handleExportRequest((ExportRequest)msg.obj);
68d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawa                    break;
69d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawa                }
7018b5190d6ed37be04d153a5d6f205076b38ac479Daisuke Miyakawa                case MSG_CANCEL_IMPORT_REQUEST: {
717c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                    handleCancelAllImportRequest();
7218b5190d6ed37be04d153a5d6f205076b38ac479Daisuke Miyakawa                    break;
7318b5190d6ed37be04d153a5d6f205076b38ac479Daisuke Miyakawa                }
747c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                // TODO: add cancel capability for export..
75d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawa                default: {
76aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa                    Log.w(LOG_TAG, "Received unknown request, ignoring it.");
77476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa                    super.hasMessages(msg.what);
78d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawa                }
79476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa            }
80f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa        }
81476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa    }
82f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa
837c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private final Handler mHandler = new RequestHandler();
847c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private final Messenger mMessenger = new Messenger(mHandler);
857c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    // Should be single thread, as we don't want to simultaneously handle import and export
867c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    // requests.
877c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
887c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
897c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private int mCurrentJobId;
907c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private final Map<Integer, ImportProcessor> mRunningJobMapForImport =
917c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            new HashMap<Integer, ImportProcessor>();
927c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private final Map<Integer, ExportProcessor> mRunningJobMapForExport =
937c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            new HashMap<Integer, ExportProcessor>();
94476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa
95476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa    @Override
96476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa    public int onStartCommand(Intent intent, int flags, int id) {
97476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa        return START_STICKY;
98f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa    }
99f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa
100f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa    @Override
101f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa    public IBinder onBind(Intent intent) {
102476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa        return mMessenger.getBinder();
103f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa    }
104aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa
105aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa    @Override
106aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa    public void onDestroy() {
1077c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        Log.i(LOG_TAG, "VCardService is finishing()");
1087c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        cancelRequestsAndshutdown();
109aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa        clearCache();
110aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa        super.onDestroy();
111aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa    }
112aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa
1137c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private synchronized void handleImportRequest(ImportRequest request) {
1147c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        Log.i(LOG_TAG, String.format("Received vCard import request. id: %d", mCurrentJobId));
1157c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        final ImportProcessor importProcessor =
1167c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                new ImportProcessor(this, request, mCurrentJobId);
1177c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        try {
1187c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            mExecutorService.submit(importProcessor);
1197c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        } catch (RejectedExecutionException e) {
1207c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            Log.w(LOG_TAG, "vCard import request is rejected.", e);
1217c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            // TODO: a little unkind to show Toast in this case, which is shown just a moment.
1227c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            // Ideally we should show some persistent something users can notice more easily.
1237c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            Toast.makeText(this, getString(R.string.vcard_import_request_rejected_message),
1247c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                    Toast.LENGTH_LONG).show();
1257c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            return;
1267c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        }
1277c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        mRunningJobMapForImport.put(mCurrentJobId, importProcessor);
1287c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        mCurrentJobId++;
1297c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        // TODO: Ideally we should detect the current status of import/export and show "started"
1307c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        // when we can import right now and show "will start" when we cannot.
1317c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        Toast.makeText(this, getString(R.string.vcard_import_will_start_message),
1327c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                Toast.LENGTH_LONG).show();
1337c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    }
1347c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
1357c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private synchronized void handleExportRequest(ExportRequest request) {
1367c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        Log.i(LOG_TAG, String.format("Received vCard export request. id: %d", mCurrentJobId));
1377c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        final ExportProcessor exportProcessor =
1387c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                new ExportProcessor(this, request, mCurrentJobId);
1397c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        try {
1407c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            mExecutorService.submit(exportProcessor);
1417c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        } catch (RejectedExecutionException e) {
1427c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            Log.w(LOG_TAG, "vCard export request is rejected.", e);
1437c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            Toast.makeText(this, getString(R.string.vcard_export_request_rejected_message),
1447c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                    Toast.LENGTH_LONG).show();
1457c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            return;
1467c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        }
1477c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        mRunningJobMapForExport.put(mCurrentJobId, exportProcessor);
1487c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        mCurrentJobId++;
1497c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        // See the comment in handleImportRequest()
1507c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        Toast.makeText(this, getString(R.string.vcard_export_will_start_message),
1517c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                Toast.LENGTH_LONG).show();
1527c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    }
1537c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
1547c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private synchronized void handleCancelAllImportRequest() {
1557c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        Log.i(LOG_TAG, "Received cancel import request.");
1567c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        cancelAllImportRequest();
1577c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        mRunningJobMapForImport.clear();
1587c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    }
1597c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
1607c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private void cancelAllImportRequest() {
1617c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        for (final Map.Entry<Integer, ImportProcessor> entry :
1627c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                mRunningJobMapForImport.entrySet()) {
1637c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            final int jobId = entry.getKey();
1647c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            final ImportProcessor importProcessor = entry.getValue();
1657c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            importProcessor.cancel();
1667c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            Log.i(LOG_TAG, String.format("Canceling job %d", jobId));
1677c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        }
1687c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    }
1697c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
1707c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private void cancelAllExportRequest() {
1717c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        for (final Map.Entry<Integer, ExportProcessor> entry :
1727c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                mRunningJobMapForExport.entrySet()) {
1737c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            final int jobId = entry.getKey();
1747c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            final ExportProcessor exportProcessor = entry.getValue();
1757c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            exportProcessor.cancel();
1767c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            Log.i(LOG_TAG, String.format("Canceling job %d", jobId));
1777c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        }
1787c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    }
1797c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
1807c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    /* package */ synchronized void handleFinishImportNotification(
1817c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            int jobId, boolean successful) {
1827c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        Log.i(LOG_TAG, String.format("Received vCard import finish notification (id: %d). "
1837c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                + "Result: %b", jobId, (successful ? "success" : "failure")));
1847c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        if (mRunningJobMapForImport.remove(jobId) == null) {
1857c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            Log.w(LOG_TAG, String.format("Tried to remove unknown job (id: %d)", jobId));
1867c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        }
1877c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    }
1887c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
1897c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    /* package */ synchronized void handleFinishExportNotification(
1907c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            int jobId, boolean successful) {
1917c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        Log.i(LOG_TAG, String.format("Received vCard export finish notification (id: %d). "
1927c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                + "Result: %b", jobId, (successful ? "success" : "failure")));
1937c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        if (mRunningJobMapForExport.remove(jobId) == null) {
1947c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            Log.w(LOG_TAG, String.format("Tried to remove unknown job (id: %d)", jobId));
1957c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        }
1967c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    }
1977c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
1987c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    /**
1997c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa     * Cancels all the import/export requests and call {@link ExecutorService#shutdown()}, which
2007c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa     * means this Service becomes no longer ready for import/export requests. Mainly used in
2017c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa     * onDestroy().
2027c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa     */
2037c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private synchronized void cancelRequestsAndshutdown() {
2047c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        synchronized (this) {
2057c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            if (mRunningJobMapForImport.size() > 0) {
2067c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                Log.i(LOG_TAG,
2077c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                        String.format("Cancel existing all import requests (remains: ",
2087c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                                mRunningJobMapForImport.size()));
2097c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                cancelAllImportRequest();
2107c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            }
2117c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            if (mRunningJobMapForExport.size() > 0) {
2127c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                Log.i(LOG_TAG,
2137c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                        String.format("Cancel existing all import requests (remains: ",
2147c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                                mRunningJobMapForExport.size()));
2157c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                cancelAllExportRequest();
2167c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            }
2177c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            mExecutorService.shutdown();
2187c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        }
2197c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    }
2207c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
2217c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    /**
2227c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa     * Removes import caches stored locally.
2237c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa     */
224aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa    private void clearCache() {
225aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa        Log.i(LOG_TAG, "start removing cache files if exist.");
226aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa        final String[] fileLists = fileList();
227aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa        for (String fileName : fileLists) {
228aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa            if (fileName.startsWith(CACHE_FILE_PREFIX)) {
229aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa                // We don't want to keep all the caches so we remove cache files old enough.
230aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa                // TODO: Ideally we should ask VCardService whether the file is being used or
231aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa                // going to be used.
232aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa                final Date now = new Date();
233aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa                final File file = getFileStreamPath(fileName);
234aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa                Log.i(LOG_TAG, "Remove a temporary file: " + fileName);
235aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa                deleteFile(fileName);
236aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa            }
237aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa        }
238aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa    }
239f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa}
240