VCardService.java revision 910d3e7854e657d20ab8c3a5a330b2a3188b1c74
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
297c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawaimport java.util.HashMap;
307c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawaimport java.util.Map;
317c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawaimport java.util.concurrent.ExecutorService;
327c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawaimport java.util.concurrent.Executors;
337c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawaimport java.util.concurrent.RejectedExecutionException;
341b918e58f4a3ae8d32af83f6f69bbf2de57a94f9Daisuke Miyakawa
35f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa/**
367c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa * The class responsible for handling vCard import/export requests.
377c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa *
387c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa * This Service creates one ImportRequest/ExportRequest object (as Runnable) per request and push
397c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa * it to {@link ExecutorService} with single thread executor. The executor handles each request
407c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa * one by one, and notifies users when needed.
41f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa */
427c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa// TODO: Using IntentService looks simpler than using Service + ServiceConnection though this
437c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa// works fine enough. Investigate the feasibility.
44d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawapublic class VCardService extends Service {
457c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private final static String LOG_TAG = "VCardService";
46f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa
47ef41f8866e8e7d52e04907f7282adcf5f4749f25Daisuke Miyakawa    /* package */ static final int MSG_IMPORT_REQUEST = 1;
48d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawa    /* package */ static final int MSG_EXPORT_REQUEST = 2;
4918b5190d6ed37be04d153a5d6f205076b38ac479Daisuke Miyakawa    /* package */ static final int MSG_CANCEL_IMPORT_REQUEST = 3;
50f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa
51d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawa    /* package */ static final int IMPORT_NOTIFICATION_ID = 1000;
52d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawa    /* package */ static final int EXPORT_NOTIFICATION_ID = 1001;
53f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa
54aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa    /* package */ static final String CACHE_FILE_PREFIX = "import_tmp_";
55f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa
56910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa    private final Messenger mMessenger = new Messenger(new Handler() {
57476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa        @Override
58476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa        public void handleMessage(Message msg) {
59476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa            switch (msg.what) {
60ef41f8866e8e7d52e04907f7282adcf5f4749f25Daisuke Miyakawa                case MSG_IMPORT_REQUEST: {
617c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                    handleImportRequest((ImportRequest)msg.obj);
62476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa                    break;
63915723665339c73c9bdebc7bf8ef56c414602c2cDaisuke Miyakawa                }
64d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawa                case MSG_EXPORT_REQUEST: {
657c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                    handleExportRequest((ExportRequest)msg.obj);
66d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawa                    break;
67d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawa                }
6818b5190d6ed37be04d153a5d6f205076b38ac479Daisuke Miyakawa                case MSG_CANCEL_IMPORT_REQUEST: {
697c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                    handleCancelAllImportRequest();
7018b5190d6ed37be04d153a5d6f205076b38ac479Daisuke Miyakawa                    break;
7118b5190d6ed37be04d153a5d6f205076b38ac479Daisuke Miyakawa                }
727c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                // TODO: add cancel capability for export..
73d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawa                default: {
74aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa                    Log.w(LOG_TAG, "Received unknown request, ignoring it.");
75476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa                    super.hasMessages(msg.what);
76d8fb81a0024d30c027ea6ebf57d29d3ff10453fbDaisuke Miyakawa                }
77476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa            }
78f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa        }
79910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa    });
80f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa
817c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    // Should be single thread, as we don't want to simultaneously handle import and export
827c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    // requests.
83910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa    private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
847c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
857c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private int mCurrentJobId;
86910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa
87910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa    // Stores all unfinished import/export jobs which will be executed by mExecutorService.
88910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa    // Key is jobId.
89910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa    private final Map<Integer, ProcessorBase> mRunningJobMap =
90910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            new HashMap<Integer, ProcessorBase>();
91476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa
92476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa    @Override
93476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa    public int onStartCommand(Intent intent, int flags, int id) {
94476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa        return START_STICKY;
95f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa    }
96f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa
97f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa    @Override
98f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa    public IBinder onBind(Intent intent) {
99476004be0c4907b462b3d699671d9e1cff1a7bd7Daisuke Miyakawa        return mMessenger.getBinder();
100f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa    }
101aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa
102aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa    @Override
103aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa    public void onDestroy() {
104910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa        Log.i(LOG_TAG, "VCardService is being destroyed.");
105910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa        cancelAllRequestsAndShutdown();
106aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa        clearCache();
107aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa        super.onDestroy();
108aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa    }
109aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa
1107c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private synchronized void handleImportRequest(ImportRequest request) {
111910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa        tryExecute(new ImportProcessor(this, request, mCurrentJobId),
112910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa                R.string.vcard_import_will_start_message,
113910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa                R.string.vcard_import_request_rejected_message);
1147c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    }
1157c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
1167c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    private synchronized void handleExportRequest(ExportRequest request) {
117910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa        tryExecute(new ExportProcessor(this, request, mCurrentJobId),
118910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa                R.string.vcard_export_will_start_message,
119910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa                R.string.vcard_export_request_rejected_message);
120910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa    }
121910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa
122910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa    /**
123910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa     * Tries to call {@link ExecutorService#execute(Runnable)} toward a given processor and
124910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa     * shows appropriate Toast using given resource ids.
125910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa     * Updates relevant instances when successful.
126910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa     */
127910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa    private synchronized void tryExecute(ProcessorBase processor,
128910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            int successfulMessageId, int rejectedMessageId) {
1297c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        try {
130910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            mExecutorService.execute(processor);
131910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            mRunningJobMap.put(mCurrentJobId, processor);
132910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            mCurrentJobId++;
133910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            // TODO: Ideally we should detect the current status of import/export and show
134910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            // "started" when we can import right now and show "will start" when we cannot.
135910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            Toast.makeText(this, getString(successfulMessageId), Toast.LENGTH_LONG).show();
1367c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        } catch (RejectedExecutionException e) {
137910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            Log.w(LOG_TAG, "Failed to excetute a job.", e);
138910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            // TODO: a little unkind to show Toast in this case, which is shown just a moment.
139910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            // Ideally we should show some persistent something users can notice more easily.
140910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            Toast.makeText(this, getString(rejectedMessageId), Toast.LENGTH_LONG).show();
1417c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        }
1427c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    }
1437c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
144f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa    private void handleCancelAllImportRequest() {
1457c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        Log.i(LOG_TAG, "Received cancel import request.");
146910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa        cancelAllImportRequests();
1477c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    }
1487c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
149910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa    private synchronized void cancelAllImportRequests() {
150910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa        for (final Map.Entry<Integer, ProcessorBase> entry : mRunningJobMap.entrySet()) {
151910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            final ProcessorBase processor = entry.getValue();
152910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            if (processor.getType() == ProcessorBase.PROCESSOR_TYPE_IMPORT) {
153910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa                final int jobId = entry.getKey();
154910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa                processor.cancel(true);
155910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa                mRunningJobMap.remove(jobId);
156910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa                Log.i(LOG_TAG, String.format("Canceling job %d", jobId));
157f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa            }
1587c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        }
159f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa        stopServiceWhenNoJob();
1607c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    }
1617c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
162910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa    private synchronized void cancelAllExportRequests() {
163910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa        for (final Map.Entry<Integer, ProcessorBase> entry : mRunningJobMap.entrySet()) {
164910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            final ProcessorBase processor = entry.getValue();
165910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            if (processor.getType() == ProcessorBase.PROCESSOR_TYPE_EXPORT) {
166910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa                final int jobId = entry.getKey();
167910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa                processor.cancel(true);
168910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa                mRunningJobMap.remove(jobId);
169910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa                Log.i(LOG_TAG, String.format("Canceling job %d", jobId));
170f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa            }
1717c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        }
172f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa        stopServiceWhenNoJob();
173f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa    }
174f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa
175f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa    /**
176f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa     * Checks job list and call {@link #stopSelf()} when there's no job now.
177f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa     * A new job cannot be submitted any more after this call.
178f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa     */
179f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa    private synchronized void stopServiceWhenNoJob() {
180910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa        if (mRunningJobMap.size() > 0) {
181910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            for (final Map.Entry<Integer, ProcessorBase> entry : mRunningJobMap.entrySet()) {
182f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa                final int jobId = entry.getKey();
183910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa                final ProcessorBase processor = entry.getValue();
184910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa                if (processor.isDone()) {
185910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa                    mRunningJobMap.remove(jobId);
186f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa                } else {
187f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa                    Log.i(LOG_TAG, String.format("Found unfinished job (id: %d)", jobId));
188f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa                    return;
189f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa                }
190f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa            }
191f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa        }
192f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa
193f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa        Log.i(LOG_TAG, "No unfinished job. Stop this service.");
194f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa        mExecutorService.shutdown();
195f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa        stopSelf();
1967c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    }
1977c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
1987c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    /* package */ synchronized void handleFinishImportNotification(
1997c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            int jobId, boolean successful) {
200910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa        Log.v(LOG_TAG, String.format("Received vCard import finish notification (id: %d). "
2017c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                + "Result: %b", jobId, (successful ? "success" : "failure")));
202910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa        if (mRunningJobMap.remove(jobId) == null) {
2037c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            Log.w(LOG_TAG, String.format("Tried to remove unknown job (id: %d)", jobId));
2047c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        }
205f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa        stopServiceWhenNoJob();
2067c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    }
2077c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
2087c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    /* package */ synchronized void handleFinishExportNotification(
2097c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            int jobId, boolean successful) {
210910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa        Log.v(LOG_TAG, String.format("Received vCard export finish notification (id: %d). "
2117c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa                + "Result: %b", jobId, (successful ? "success" : "failure")));
212910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa        if (mRunningJobMap.remove(jobId) == null) {
2137c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa            Log.w(LOG_TAG, String.format("Tried to remove unknown job (id: %d)", jobId));
2147c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        }
215f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa        stopServiceWhenNoJob();
2167c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    }
2177c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
2187c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    /**
219910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa     * Cancels all the import/export requests and calls {@link ExecutorService#shutdown()}, which
220f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa     * means this Service becomes no longer ready for import/export requests.
221f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa     *
222f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa     * Mainly called from onDestroy().
2237c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa     */
224910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa    private synchronized void cancelAllRequestsAndShutdown() {
225910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa        for (final Map.Entry<Integer, ProcessorBase> entry : mRunningJobMap.entrySet()) {
226910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa            entry.getValue().cancel(true);
2277c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa        }
228910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa        mRunningJobMap.clear();
229f219f6ee48c503bb2b628c3f2aeff53b15c5a947Daisuke Miyakawa        mExecutorService.shutdown();
2307c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    }
2317c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa
2327c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa    /**
2337c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa     * Removes import caches stored locally.
2347c819a1a434e02c54f6d216aa3b1a0d08cc93f50Daisuke Miyakawa     */
235aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa    private void clearCache() {
236910d3e7854e657d20ab8c3a5a330b2a3188b1c74Daisuke Miyakawa        for (final String fileName : fileList()) {
237aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa            if (fileName.startsWith(CACHE_FILE_PREFIX)) {
238aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa                // We don't want to keep all the caches so we remove cache files old enough.
239aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa                Log.i(LOG_TAG, "Remove a temporary file: " + fileName);
240aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa                deleteFile(fileName);
241aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa            }
242aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa        }
243aba4353bcf77ba91463cfd079feec3b6c6f59c5cDaisuke Miyakawa    }
244f21bf27c13dacec9b4ed74cba9046a64948e97fbDaisuke Miyakawa}
245