Skip to content

Most visited

Recently visited

navigation
BluetoothAdvertisements / src / com.example.android.bluetoothadvertisements /

AdvertiserService.java

1
package com.example.android.bluetoothadvertisements;
2
 
3
import android.app.Service;
4
import android.bluetooth.BluetoothAdapter;
5
import android.bluetooth.BluetoothManager;
6
import android.bluetooth.le.AdvertiseCallback;
7
import android.bluetooth.le.AdvertiseData;
8
import android.bluetooth.le.AdvertiseSettings;
9
import android.bluetooth.le.BluetoothLeAdvertiser;
10
import android.content.Context;
11
import android.content.Intent;
12
import android.os.Handler;
13
import android.os.IBinder;
14
import android.util.Log;
15
import android.widget.Toast;
16
 
17
import java.util.concurrent.TimeUnit;
18
 
19
/**
20
 * Manages BLE Advertising independent of the main app.
21
 * If the app goes off screen (or gets killed completely) advertising can continue because this
22
 * Service is maintaining the necessary Callback in memory.
23
 */
24
public class AdvertiserService extends Service {
25
 
26
    private static final String TAG = AdvertiserService.class.getSimpleName();
27
 
28
    /**
29
     * A global variable to let AdvertiserFragment check if the Service is running without needing
30
     * to start or bind to it.
31
     * This is the best practice method as defined here:
32
     * https://groups.google.com/forum/#!topic/android-developers/jEvXMWgbgzE
33
     */
34
    public static boolean running = false;
35
 
36
    public static final String ADVERTISING_FAILED =
37
            "com.example.android.bluetoothadvertisements.advertising_failed";
38
 
39
    public static final String ADVERTISING_FAILED_EXTRA_CODE = "failureCode";
40
 
41
    public static final int ADVERTISING_TIMED_OUT = 6;
42
 
43
    private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
44
 
45
    private AdvertiseCallback mAdvertiseCallback;
46
 
47
    private Handler mHandler;
48
 
49
    private Runnable timeoutRunnable;
50
 
51
    /**
52
     * Length of time to allow advertising before automatically shutting off. (10 minutes)
53
     */
54
    private long TIMEOUT = TimeUnit.MILLISECONDS.convert(10, TimeUnit.MINUTES);
55
 
56
    @Override
57
    public void onCreate() {
58
        running = true;
59
        initialize();
60
        startAdvertising();
61
        setTimeout();
62
        super.onCreate();
63
    }
64
 
65
    @Override
66
    public void onDestroy() {
67
        /**
68
         * Note that onDestroy is not guaranteed to be called quickly or at all. Services exist at
69
         * the whim of the system, and onDestroy can be delayed or skipped entirely if memory need
70
         * is critical.
71
         */
72
        running = false;
73
        stopAdvertising();
74
        mHandler.removeCallbacks(timeoutRunnable);
75
        super.onDestroy();
76
    }
77
 
78
    /**
79
     * Required for extending service, but this will be a Started Service only, so no need for
80
     * binding.
81
     */
82
    @Override
83
    public IBinder onBind(Intent intent) {
84
        return null;
85
    }
86
 
87
    /**
88
     * Get references to system Bluetooth objects if we don't have them already.
89
     */
90
    private void initialize() {
91
        if (mBluetoothLeAdvertiser == null) {
92
            BluetoothManager mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
93
            if (mBluetoothManager != null) {
94
                BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter();
95
                if (mBluetoothAdapter != null) {
96
                    mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
97
                } else {
98
                    Toast.makeText(this, getString(R.string.bt_null), Toast.LENGTH_LONG).show();
99
                }
100
            } else {
101
                Toast.makeText(this, getString(R.string.bt_null), Toast.LENGTH_LONG).show();
102
            }
103
        }
104
 
105
    }
106
 
107
    /**
108
     * Starts a delayed Runnable that will cause the BLE Advertising to timeout and stop after a
109
     * set amount of time.
110
     */
111
    private void setTimeout(){
112
        mHandler = new Handler();
113
        timeoutRunnable = new Runnable() {
114
            @Override
115
            public void run() {
116
                Log.d(TAG, "AdvertiserService has reached timeout of "+TIMEOUT+" milliseconds, stopping advertising.");
117
                sendFailureIntent(ADVERTISING_TIMED_OUT);
118
                stopSelf();
119
            }
120
        };
121
        mHandler.postDelayed(timeoutRunnable, TIMEOUT);
122
    }
123
 
124
    /**
125
     * Starts BLE Advertising.
126
     */
127
    private void startAdvertising() {
128
        Log.d(TAG, "Service: Starting Advertising");
129
 
130
        if (mAdvertiseCallback == null) {
131
            AdvertiseSettings settings = buildAdvertiseSettings();
132
            AdvertiseData data = buildAdvertiseData();
133
            mAdvertiseCallback = new SampleAdvertiseCallback();
134
 
135
            if (mBluetoothLeAdvertiser != null) {
136
                mBluetoothLeAdvertiser.startAdvertising(settings, data,
137
                        mAdvertiseCallback);
138
            }
139
        }
140
    }
141
 
142
    /**
143
     * Stops BLE Advertising.
144
     */
145
    private void stopAdvertising() {
146
        Log.d(TAG, "Service: Stopping Advertising");
147
        if (mBluetoothLeAdvertiser != null) {
148
            mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
149
            mAdvertiseCallback = null;
150
        }
151
    }
152
 
153
    /**
154
     * Returns an AdvertiseData object which includes the Service UUID and Device Name.
155
     */
156
    private AdvertiseData buildAdvertiseData() {
157
 
158
        /**
159
         * Note: There is a strict limit of 31 Bytes on packets sent over BLE Advertisements.
160
         *  This includes everything put into AdvertiseData including UUIDs, device info, &
161
         *  arbitrary service or manufacturer data.
162
         *  Attempting to send packets over this limit will result in a failure with error code
163
         *  AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE. Catch this error in the
164
         *  onStartFailure() method of an AdvertiseCallback implementation.
165
         */
166
 
167
        AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
168
        dataBuilder.addServiceUuid(Constants.Service_UUID);
169
        dataBuilder.setIncludeDeviceName(true);
170
 
171
        /* For example - this will cause advertising to fail (exceeds size limit) */
172
        //String failureData = "asdghkajsghalkxcjhfa;sghtalksjcfhalskfjhasldkjfhdskf";
173
        //dataBuilder.addServiceData(Constants.Service_UUID, failureData.getBytes());
174
 
175
        return dataBuilder.build();
176
    }
177
 
178
    /**
179
     * Returns an AdvertiseSettings object set to use low power (to help preserve battery life)
180
     * and disable the built-in timeout since this code uses its own timeout runnable.
181
     */
182
    private AdvertiseSettings buildAdvertiseSettings() {
183
        AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
184
        settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER);
185
        settingsBuilder.setTimeout(0);
186
        return settingsBuilder.build();
187
    }
188
 
189
    /**
190
     * Custom callback after Advertising succeeds or fails to start. Broadcasts the error code
191
     * in an Intent to be picked up by AdvertiserFragment and stops this Service.
192
     */
193
    private class SampleAdvertiseCallback extends AdvertiseCallback {
194
 
195
        @Override
196
        public void onStartFailure(int errorCode) {
197
            super.onStartFailure(errorCode);
198
 
199
            Log.d(TAG, "Advertising failed");
200
            sendFailureIntent(errorCode);
201
            stopSelf();
202
 
203
        }
204
 
205
        @Override
206
        public void onStartSuccess(AdvertiseSettings settingsInEffect) {
207
            super.onStartSuccess(settingsInEffect);
208
            Log.d(TAG, "Advertising successfully started");
209
        }
210
    }
211
 
212
    /**
213
     * Builds and sends a broadcast intent indicating Advertising has failed. Includes the error
214
     * code as an extra. This is intended to be picked up by the {@code AdvertiserFragment}.
215
     */
216
    private void sendFailureIntent(int errorCode){
217
        Intent failureIntent = new Intent();
218
        failureIntent.setAction(ADVERTISING_FAILED);
219
        failureIntent.putExtra(ADVERTISING_FAILED_EXTRA_CODE, errorCode);
220
        sendBroadcast(failureIntent);
221
    }
222
 
223
}
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!