Skip to content

Android Cloud Phone

The JS AIRA SDK provides a comprehensive solution for integrating Android cloud phone instances into web applications. This guide demonstrates how to implement real-time screen casting, device control, and interactive features.

Android cloud phones offer virtualized mobile environments that can be controlled remotely through web browsers. The JS AIRA SDK enables developers to:

  • Stream Android screens in real-time with high quality
  • Control devices remotely including touch input, hardware buttons, and text input
  • Handle multi-user scenarios with permission management
  • Integrate seamlessly with modern web frameworks

Add the SDK script tag to your HTML file (before your application code):

<script src="https://unpkg.com/@agentbox-sdk/aira@2.16.2/dist/index.js"></script>
// Initialize SDK instance (using global variable from CDN)
const sdk = new window.NzCp();

// Configure connection parameters
const config = {
  userId: 'unique-user-id',     // Your user identifier
  mountId: 'android-container', // DOM element ID for rendering
  instanceNo: 'instance-id',    // Instance number (required)
  width: 720,                   // Screen width
  height: 1280,                 // Screen height
  bitrate: 2048,                // Video quality
  fps: 30                       // Frame rate
};

// Set up event handlers
const callbacks = {
  onStartSuccess: () => console.log('Connected to Android instance'),
  onStartFail: (code) => console.error('Connection failed:', code),
  onError: (code) => console.error('Runtime error:', code)
};

// Initialize and start
sdk.init(config, callbacks);
sdk.start('your-access-key', 'your-secret-key');
// Hardware button simulation
sdk.home();     // Home button
sdk.back();     // Back button  
sdk.menu();     // Menu button

// Text input and clipboard
sdk.sendText('Hello World');
sdk.sendInClipper('Clipboard content');

// Cleanup when done
sdk.destroy();
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Android Cloud Phone - AIRA SDK</title>
    <script src="https://unpkg.com/@agentbox-sdk/aira@2.16.2/dist/index.js"></script>
    <style>
        body { margin: 0; font-family: Arial, sans-serif; }
        #android-container { width: 100%; height: 100vh; background: #000; }
        .controls { 
            position: fixed; 
            bottom: 20px; 
            left: 20px; 
            z-index: 1000;
            background: rgba(0,0,0,0.7);
            padding: 10px;
            border-radius: 8px;
        }
        .controls button {
            margin: 0 5px;
            padding: 8px 16px;
            background: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .controls button:hover { background: #0056b3; }
        .play-button {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            padding: 15px 30px;
            font-size: 18px;
            background: #28a745;
            color: white;
            border: none;
            border-radius: 8px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <!-- Play button (shown before connection) -->
    <button id="playBtn" class="play-button">Connect to Android</button>
    
    <!-- Device controls (shown after connection) -->
    <div id="controls" class="controls" style="display: none;">
        <button id="backBtn">Back</button>
        <button id="homeBtn">Home</button>
        <button id="menuBtn">Menu</button>
        <button id="stopBtn">Disconnect</button>
    </div>
    
    <!-- Android screen container -->
    <div id="android-container"></div>

    <script type="module">
        class AndroidController {
            constructor() {
                this.sdk = null;
                this.isConnected = false;
                this.setupEventListeners();
            }

            setupEventListeners() {
                document.getElementById('playBtn').addEventListener('click', () => this.connect());
                document.getElementById('backBtn').addEventListener('click', () => this.pressBack());
                document.getElementById('homeBtn').addEventListener('click', () => this.pressHome());
                document.getElementById('menuBtn').addEventListener('click', () => this.pressMenu());
                document.getElementById('stopBtn').addEventListener('click', () => this.disconnect());
            }

            async connect() {
                try {
                    // Get credentials from your backend
                    const credentials = await this.getCredentials();
                    
                    // Use global variable from CDN script tag
                    this.sdk = new window.NzCp();
                    
                    const config = {
                        userId: 'user-123',
                        mountId: 'android-container',
                        instanceNo: 'instance-123',
                        width: 720,
                        height: 1280,
                        bitrate: 2048,
                        fps: 30
                    };

                    const callbacks = {
                        onStartSuccess: () => {
                            console.log('✅ Connected to Android instance');
                            this.onConnected();
                        },
                        onStartFail: (code) => {
                            console.error('❌ Connection failed:', code);
                            this.onConnectionFailed(code);
                        },
                        onError: (code) => {
                            console.error('⚠️ Runtime error:', code);
                        },
                        onStop: () => {
                            console.log('📱 Android session ended');
                            this.onDisconnected();
                        }
                    };

                    const initSuccess = this.sdk.init(config, callbacks);
                    if (!initSuccess) {
                        throw new Error('SDK initialization failed');
                    }

                    // Start the connection
                    this.sdk.start(credentials.accessKey, credentials.secretKey);
                    
                } catch (error) {
                    console.error('Connection error:', error);
                    alert('Failed to connect: ' + error.message);
                }
            }

            async getCredentials() {
                // Mock implementation for testing - Replace with your actual backend endpoint
                // const response = await fetch('/api/instance/security-token', {
                //     method: 'POST',
                //     headers: {
                //         'Content-Type': 'application/json',
                //         'Platform-Access-Key': 'your-platform-key',
                //         'Platform-Request-DateTime': Date.now().toString(),
                //         'Platform-Request-Signature': 'calculated-signature'
                //     },
                //     body: JSON.stringify({
                //         userId: 'user-123',
                //         validTime: 3600
                //     })
                // });
                // if (!response.ok) {
                //     throw new Error('Failed to get credentials');
                // }
                // const result = await response.json();
                // return {
                //     accessKey: result.data.accessKey,
                //     secretKey: result.data.accessSecretKey
                // };

                // Mock response for testing
                return new Promise((resolve) => {
                    setTimeout(() => {
                        resolve({
                            accessKey: 'mock_access_key_' + Date.now(),
                            secretKey: 'mock_secret_key_' + Date.now()
                        });
                    }, 500); // Simulate network delay
                });
            }

            onConnected() {
                this.isConnected = true;
                document.getElementById('playBtn').style.display = 'none';
                document.getElementById('controls').style.display = 'block';
            }

            onConnectionFailed(code) {
                alert(`Connection failed with error code: ${code}`);
            }

            onDisconnected() {
                this.isConnected = false;
                document.getElementById('playBtn').style.display = 'block';
                document.getElementById('controls').style.display = 'none';
            }

            pressBack() {
                if (this.sdk && this.isConnected) {
                    this.sdk.back();
                }
            }

            pressHome() {
                if (this.sdk && this.isConnected) {
                    this.sdk.home();
                }
            }

            pressMenu() {
                if (this.sdk && this.isConnected) {
                    this.sdk.menu();
                }
            }

            disconnect() {
                if (this.sdk) {
                    this.sdk.destroy();
                    this.sdk = null;
                    this.onDisconnected();
                }
            }
        }

        // Initialize the controller
        new AndroidController();
    </script>
</body>
</html>

To get instance operation credentials, use the following API endpoint:

Endpoint: POST /api/instance/security-token
Content-Type: application/json

Description: Before using the SDK to operate instances, call this API with the user ID to get authentication credentials (accessKey/accessSecretKey), then pass them to the SDK for initialization. The SDK can only operate instances assigned to the specified user ID.

Request Headers:

NameTypeRequiredDescription
Platform-Access-KeystringPlatform access key
Platform-Request-DateTimestring13-digit timestamp of the request
Platform-Request-SignaturestringCalculated signature based on request parameters

Request Body:

ParameterTypeRequiredDescription
userIdstringUser ID - unique user identifier defined by your business logic. Can be a user’s unique identity or a timestamp-generated ID. SDK can only operate instances assigned to this user ID.
validTimeint32Credential validity duration in seconds (default: 3600/1 hour, minimum: 10 seconds)
useOncebooleanWhether credentials are single-use (default: false). If true, credentials become invalid after successful authentication
instanceNosarrayArray of instance IDs that can be operated. User can only operate instances in this list

Response:

{
  "data": {
    "accessKey": "temp_access_key_here",
    "accessSecretKey": "temp_secret_key_here", 
    "expireTime": "2024-11-07 15:30:00"
  },
  "code": 0,
  "msg": "Success",
  "times": 1699363800000
}

Response Fields:

FieldTypeDescription
data.accessKeystringTemporary access key
data.accessSecretKeystringTemporary secret key
data.expireTimestringExpiration time (format: yyyy-MM-dd HH:mm:ss)
codeint32Response code (0 = success, non-zero = error)
msgstringResponse message
timesint64Response timestamp

Example Implementation:

// ❌ Don't do this - credentials exposed
const credentials = {
  accessKey: 'your-key-here',
  secretKey: 'your-secret-here'
};

// ✅ Do this - fetch from secure backend
async function getInstanceCredentials(userId) {
  const response = await fetch('/api/instance/security-token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Platform-Access-Key': 'your-platform-key',
      'Platform-Request-DateTime': Date.now().toString(),
      'Platform-Request-Signature': 'calculated-signature'
    },
    body: JSON.stringify({
      userId: userId,
      validTime: 3600, // 1 hour
      useOnce: false
    })
  });

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
  }

  const result = await response.json();
  if (result.code !== 0) {
    throw new Error(`API Error: ${result.msg}`);
  }

  return {
    accessKey: result.data.accessKey,
    secretKey: result.data.accessSecretKey,
    expireTime: result.data.expireTime
  };
}

// Use in your application
const credentials = await getInstanceCredentials('user-123');
sdk.start(credentials.accessKey, credentials.secretKey);
  • Adjust bitrate based on network conditions (1-2 Mbps for mobile, 2-4 Mbps for desktop)
  • Monitor stream quality using the onPlayInfo callback
  • Implement reconnection logic for network interruptions
  • Clean up resources when components unmount
const callbacks = {
  onStartFail: (code) => {
    switch(code) {
      case 60401002:
        console.error('Invalid parameters - check configuration');
        break;
      case 60401003:
        console.error('Network error - check connectivity');
        break;
      case 60100014:
        console.error('Authentication failed - refresh credentials');
        break;
      default:
        console.error(`Unknown error: ${code}`);
    }
  }
};