• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#
2# Copyright (C) 2018 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16import logging
17
18from vts.proto import AndroidSystemControlMessage_pb2 as ASysCtrlMsg
19from vts.proto import VtsResourceControllerMessage_pb2 as ResControlMsg
20from vts.proto import ComponentSpecificationMessage_pb2 as CompSpecMsg
21from vts.utils.python.mirror import mirror_object
22
23
24class ResourceFmqMirror(mirror_object.MirrorObject):
25    """This is a class that mirrors FMQ resource allocated on the target side.
26
27    Attributes:
28        SUPPORTED_SCALAR_TYPES: set, contains all scalar types supported by FMQ.
29                                If the type of FMQ is one of those, this class
30                                prepares the write data from caller provided
31                                Python data.
32        _client: VtsTcpClient, the TCP client instance.
33        _queue_id: int, used to identify the queue object on the target side.
34        _data_type: type of data in the queue.
35        _sync: bool, whether the queue is synchronized.
36    """
37
38    SUPPORTED_SCALAR_TYPES = {
39        "uint8_t", "int8_t", "uint16_t", "int16_t", "uint32_t", "int32_t",
40        "uint64_t", "int64_t", "bool_t", "double_t"
41    }
42
43    def __init__(self, data_type, sync, client, queue_id=-1):
44        """Initialize a FMQ mirror.
45
46        Args:
47            data_type: string, type of data in the queue
48                       (e.g. "uint32_t", "int16_t").
49            sync: bool, whether queue is synchronized (only has one reader).
50            client: VtsTcpClient, specifies the session that this mirror use.
51            queue_id: int, identifies the queue on the target side.
52                      Optional if caller initializes a new FMQ mirror.
53        """
54        super(ResourceFmqMirror, self).__init__(client)
55        self._data_type = data_type
56        self._sync = sync
57        self._queue_id = queue_id
58
59    def _create(self, queue_id, queue_size, blocking, reset_pointers):
60        """Initiate a fast message queue object on the target side.
61
62        This method registers a FMQ object on the target side, and stores
63        the queue_id in the class attribute.
64        Users should not directly call this method because it will overwrite
65        the original queue_id stored in the mirror object, leaving that
66        queue object out of reference.
67        Users should always call InitFmq() in mirror_tracker.py to obtain a
68        new queue object.
69
70        Args:
71            queue_id: int, identifies the message queue object on the target side.
72            queue_size: int, size of the queue.
73            blocking: bool, whether blocking is enabled in the queue.
74            reset_pointers: bool, whether to reset read/write pointers when
75              creating a message queue object based on an existing message queue.
76        """
77        # Prepare arguments.
78        request_msg = self._createTemplateRequestMessage(
79            ResControlMsg.FMQ_CREATE, queue_id)
80        request_msg.queue_size = queue_size
81        request_msg.blocking = blocking
82        request_msg.reset_pointers = reset_pointers
83
84        # Send and receive data.
85        fmq_response = self._client.SendFmqRequest(request_msg)
86        if fmq_response is not None and fmq_response.queue_id != -1:
87            self._queue_id = fmq_response.queue_id
88        else:
89            self._queue_id = -1
90            logging.error("Failed to create a new queue object.")
91
92    def read(self, data, data_size):
93        """Initiate a non-blocking read request to FMQ driver.
94
95        Args:
96            data: list, data to be filled by this function. The list will
97                  be emptied before the function starts to put read data into
98                  it, which is consistent with the function behavior on the
99                  target side.
100            data_size: int, length of data to read.
101
102        Returns:
103            bool, true if the operation succeeds,
104                  false otherwise.
105        """
106        # Prepare arguments.
107        del data[:]
108        request_msg = self._createTemplateRequestMessage(
109            ResControlMsg.FMQ_READ, self._queue_id)
110        request_msg.read_data_size = data_size
111
112        # Send and receive data.
113        fmq_response = self._client.SendFmqRequest(request_msg)
114        if fmq_response is not None and fmq_response.success:
115            self._extractReadData(fmq_response, data)
116            return True
117        return False
118
119    # TODO: support long-form blocking read in the future when there is use case.
120    def readBlocking(self, data, data_size, time_out_nanos=0):
121        """Initiate a blocking read request (short-form) to FMQ driver.
122
123        Args:
124            data: list, data to be filled by this function. The list will
125                  be emptied before the function starts to put read data into
126                  it, which is consistent with the function behavior on the
127                  target side.
128            data_size: int, length of data to read.
129            time_out_nanos: int, wait time (in nanoseconds) when blocking.
130                            The default value is 0 (no blocking).
131
132        Returns:
133            bool, true if the operation succeeds,
134                  false otherwise.
135        """
136        # Prepare arguments.
137        del data[:]
138        request_msg = self._createTemplateRequestMessage(
139            ResControlMsg.FMQ_READ_BLOCKING, self._queue_id)
140        request_msg.read_data_size = data_size
141        request_msg.time_out_nanos = time_out_nanos
142
143        # Send and receive data.
144        fmq_response = self._client.SendFmqRequest(request_msg)
145        if fmq_response is not None and fmq_response.success:
146            self._extractReadData(fmq_response, data)
147            return True
148        return False
149
150    def write(self, data, data_size):
151        """Initiate a non-blocking write request to FMQ driver.
152
153        Args:
154            data: list, data to be written.
155            data_size: int, length of data to write.
156                       The function will only write data up until data_size,
157                       i.e. extraneous data will be discarded.
158
159        Returns:
160            bool, true if the operation succeeds,
161                  false otherwise.
162        """
163        # Prepare arguments.
164        request_msg = self._createTemplateRequestMessage(
165            ResControlMsg.FMQ_WRITE, self._queue_id)
166        prepare_result = self._prepareWriteData(request_msg, data[:data_size])
167        if not prepare_result:
168            # Prepare write data failure, error logged in _prepareWriteData().
169            return False
170
171        # Send and receive data.
172        fmq_response = self._client.SendFmqRequest(request_msg)
173        if fmq_response is not None:
174            return fmq_response.success
175        return False
176
177    # TODO: support long-form blocking write in the future when there is use case.
178    def writeBlocking(self, data, data_size, time_out_nanos=0):
179        """Initiate a blocking write request (short-form) to FMQ driver.
180
181        Args:
182            data: list, data to be written.
183            data_size: int, length of data to write.
184                       The function will only write data up until data_size,
185                       i.e. extraneous data will be discarded.
186            time_out_nanos: int, wait time (in nanoseconds) when blocking.
187                            The default value is 0 (no blocking).
188
189        Returns:
190            bool, true if the operation succeeds,
191                  false otherwise.
192        """
193        # Prepare arguments.
194        request_msg = self._createTemplateRequestMessage(
195            ResControlMsg.FMQ_WRITE_BLOCKING, self._queue_id)
196        prepare_result = self._prepareWriteData(request_msg, data[:data_size])
197        if not prepare_result:
198            # Prepare write data failure, error logged in _prepareWriteData().
199            return False
200        request_msg.time_out_nanos = time_out_nanos
201
202        # Send and receive data.
203        fmq_response = self._client.SendFmqRequest(request_msg)
204        if fmq_response is not None:
205            return fmq_response.success
206        return False
207
208    def availableToWrite(self):
209        """Get space available to write in the queue.
210
211        Returns:
212            int, number of slots available.
213        """
214        # Prepare arguments.
215        request_msg = self._createTemplateRequestMessage(
216            ResControlMsg.FMQ_AVAILABLE_WRITE, self._queue_id)
217
218        # Send and receive data.
219        return self._processUtilMethod(request_msg)
220
221    def availableToRead(self):
222        """Get number of items available to read.
223
224        Returns:
225            int, number of items.
226        """
227        # Prepare arguments.
228        request_msg = self._createTemplateRequestMessage(
229            ResControlMsg.FMQ_AVAILABLE_READ, self._queue_id)
230
231        # Send and receive data.
232        return self._processUtilMethod(request_msg)
233
234    def getQuantumSize(self):
235        """Get size of item in the queue.
236
237        Returns:
238            int, size of item.
239        """
240        # Prepare arguments.
241        request_msg = self._createTemplateRequestMessage(
242            ResControlMsg.FMQ_GET_QUANTUM_SIZE, self._queue_id)
243
244        # send and receive data
245        return self._processUtilMethod(request_msg)
246
247    def getQuantumCount(self):
248        """Get number of items that fit in the queue.
249
250        Returns:
251            int, number of items.
252        """
253        # Prepare arguments.
254        request_msg = self._createTemplateRequestMessage(
255            ResControlMsg.FMQ_GET_QUANTUM_COUNT, self._queue_id)
256
257        # Send and receive data.
258        return self._processUtilMethod(request_msg)
259
260    def isValid(self):
261        """Check if the queue is valid.
262
263        Returns:
264            bool, true if the queue is valid.
265        """
266        # Prepare arguments.
267        request_msg = self._createTemplateRequestMessage(
268            ResControlMsg.FMQ_IS_VALID, self._queue_id)
269
270        # Send and receive data.
271        fmq_response = self._client.SendFmqRequest(request_msg)
272        if fmq_response is not None:
273            return fmq_response.success
274        return False
275
276    @property
277    def queueId(self):
278        """Gets the id assigned from the target side.
279
280        Returns:
281            int, id of the queue.
282        """
283        return self._queue_id
284
285    @property
286    def dataType(self):
287        """Get the type of data of this FMQ mirror.
288
289        Returns:
290            string, type of data in the queue
291        """
292        return self._data_type
293
294    @property
295    def sync(self):
296        """Get the synchronization option of this FMQ mirror.
297
298        Returns:
299            bool, true if the queue is synchronized (only has one reader).
300        """
301        return self._sync
302
303    def _createTemplateRequestMessage(self, operation, queue_id):
304        """Creates a template FmqRequestMessage with common arguments among
305           all FMQ operations.
306
307        Args:
308            operation: FmqOp, fmq operations.
309                       (see test/vts/proto/VtsResourceControllerMessage.proto).
310            queue_id: int, identifies the message queue object on target side.
311
312        Returns:
313            FmqRequestMessage, fmq request message.
314                (See test/vts/proto/VtsResourceControllerMessage.proto).
315        """
316        request_msg = ResControlMsg.FmqRequestMessage()
317        request_msg.operation = operation
318        request_msg.data_type = self._data_type
319        request_msg.sync = self._sync
320        request_msg.queue_id = queue_id
321        return request_msg
322
323    def _prepareWriteData(self, request_msg, data):
324        """Converts python list to repeated protobuf field.
325
326        If the type of data in the queue is a supported scalar, caller can
327        directly supply the python native value. Otherwise, caller needs to
328        supply a list of VariableSpecificationMessage.
329
330        Args:
331            request_msg: FmqRequestMessage, arguments for a FMQ operation
332                         request.
333            data: VariableSpecificationMessage list or a list of scalar values.
334                  If the type of FMQ is scalar type, caller can directly
335                  specify the Python scalar data. Otherwise, caller has to
336                  provide each item as VariableSpecificationMessage.
337
338        Returns:
339            bool, true if preparation succeeds, false otherwise.
340            This function can fail if caller doesn't provide a list of
341            VariableSpecificationMessage when type of data in the queue
342            is not a supported scalar type.
343        """
344        for curr_value in data:
345            new_message = request_msg.write_data.add()
346            if isinstance(curr_value,
347                          CompSpecMsg.VariableSpecificationMessage):
348                new_message.CopyFrom(curr_value)
349            elif self._data_type in self.SUPPORTED_SCALAR_TYPES:
350                new_message.type = CompSpecMsg.TYPE_SCALAR
351                new_message.scalar_type = self._data_type
352                setattr(new_message.scalar_value, self._data_type, curr_value)
353            else:
354                logging.error("Need to provide VariableSpecificationMessage " +
355                              "if type of data in the queue is not a " +
356                              "supported scalar type.")
357                return False
358        return True
359
360    def _extractReadData(self, response_msg, data):
361        """Extracts read data from the response message returned by client.
362
363        Args:
364            response_msg: FmqResponseMessage, contains response from FMQ driver.
365            data: list, to be filled by this function. data buffer is provided
366                  by caller, so this function will append every element to the
367                  buffer.
368        """
369        for item in response_msg.read_data:
370            data.append(self._client.GetPythonDataOfVariableSpecMsg(item))
371
372    def _processUtilMethod(self, request_msg):
373        """Sends request message and process response message for util methods
374           that return an unsigned integer,
375           e.g. availableToWrite, availableToRead.
376
377        Args:
378            request_msg: FmqRequestMessage, arguments for a FMQ operation request.
379
380        Returns: int, information about the queue,
381                 None if the operation is unsuccessful.
382        """
383        fmq_response = self._client.SendFmqRequest(request_msg)
384        if fmq_response is not None and fmq_response.success:
385            return fmq_response.sizet_return_val
386        return None
387
388
389class ResourceHidlMemoryMirror(mirror_object.MirrorObject):
390    """This class mirrors hidl_memory resource allocated on the target side.
391
392    Attributes:
393        _client: the TCP client instance.
394        _mem_id: int, used to identify the memory region on the target side.
395    """
396
397    def __init__(self, client, mem_id=-1):
398        super(ResourceHidlMemoryMirror, self).__init__(client)
399        self._mem_id = mem_id
400
401    def _allocate(self, mem_size):
402        """Initiate a hidl_memory region on the target side.
403
404        This method stores the mem_id in the class attribute.
405        Users should not directly call this method to get a new memory region,
406        because it will overwrite the original memory object with mem_id,
407        making that memory object out of reference.
408        Users should always call InitHidlMemory() in mirror_tracker.py to get
409        a new memory region.
410
411        Args:
412            mem_size: int, size of the requested memory region.
413        """
414        # Prepare arguments.
415        request_msg = self._createTemplateRequestMessage(
416            ResControlMsg.MEM_PROTO_ALLOCATE)
417        request_msg.mem_size = mem_size
418
419        # Send and receive data.
420        response_msg = self._client.SendHidlMemoryRequest(request_msg)
421        if response_msg is not None and response_msg.new_mem_id != -1:
422            self._mem_id = response_msg.new_mem_id
423        else:
424            logging.error("Failed to allocate memory region.")
425
426    def read(self):
427        """Notify that caller will read the entire memory region.
428
429        Before every actual read operation, caller must call this method
430        or readRange() first.
431
432        Returns:
433            bool, true if the operation succeeds, false otherwise.
434        """
435        request_msg = self._createTemplateRequestMessage(
436            ResControlMsg.MEM_PROTO_START_READ)
437
438        response_msg = self._client.SendHidlMemoryRequest(request_msg)
439        if response_msg is not None:
440            if not response_msg.success:
441                logging.error("Failed to find memory region with id %d",
442                              self._mem_id)
443            return response_msg.success
444        return False
445
446    def readRange(self, start, length):
447        """Notify that caller will read only part of memory region.
448
449        Notify that caller will read starting at start and
450        ending at start + length.
451        Before every actual read operation, caller must call this method
452        or read() first.
453
454        Args:
455            start: int, offset from the start of memory region to be modified.
456            length: int, number of bytes to be modified.
457
458        Returns:
459            bool, true if the operation succeeds, false otherwise.
460        """
461        request_msg = self._createTemplateRequestMessage(
462            ResControlMsg.MEM_PROTO_START_READ_RANGE)
463        request_msg.start = start
464        request_msg.length = length
465
466        response_msg = self._client.SendHidlMemoryRequest(request_msg)
467        if response_msg is not None:
468            if not response_msg.success:
469                logging.error("Failed to find memory region with id %d",
470                              self._mem_id)
471            return response_msg.success
472        return False
473
474    def update(self):
475        """Notify that caller will possibly write to all memory region.
476
477        Before every actual write operation, caller must call this method
478        or updateRange() first.
479
480        Returns:
481            bool, true if the operation succeeds, false otherwise.
482        """
483        request_msg = self._createTemplateRequestMessage(
484            ResControlMsg.MEM_PROTO_START_UPDATE)
485
486        response_msg = self._client.SendHidlMemoryRequest(request_msg)
487        if response_msg is not None:
488            if not response_msg.success:
489                logging.error("Failed to find memory region with id %d",
490                              self._mem_id)
491            return response_msg.success
492        return False
493
494    def updateRange(self, start, length):
495        """Notify that caller will only write to part of memory region.
496
497        Notify that caller will only write starting at start and
498        ending at start + length.
499        Before every actual write operation, caller must call this method
500        or update() first.
501
502        Args:
503            start: int, offset from the start of memory region to be modified.
504            length: int, number of bytes to be modified.
505
506        Returns:
507            bool, true if the operation succeeds, false otherwise.
508        """
509        request_msg = self._createTemplateRequestMessage(
510            ResControlMsg.MEM_PROTO_START_UPDATE_RANGE)
511        request_msg.start = start
512        request_msg.length = length
513
514        response_msg = self._client.SendHidlMemoryRequest(request_msg)
515        if response_msg is not None:
516            if not response_msg.success:
517                logging.error("Failed to find memory region with id %d",
518                              self._mem_id)
519            return response_msg.success
520        return False
521
522    def readBytes(self, length, start=0):
523        """This method performs actual read operation.
524
525        This method helps caller perform actual read operation on the
526        memory region, because host side won't be able to cast c++ pointers.
527
528        Args:
529            length: int, number of bytes to read.
530            start: int, offset from the start of memory region to read.
531
532        Returns:
533            string, data read from memory.
534                    Caller can perform conversion on the result to obtain the
535                    corresponding data structure in python.
536            None, indicate if the read fails.
537        """
538        request_msg = self._createTemplateRequestMessage(
539            ResControlMsg.MEM_PROTO_READ_BYTES)
540        request_msg.start = start
541        request_msg.length = length
542
543        response_msg = self._client.SendHidlMemoryRequest(request_msg)
544        if response_msg is not None:
545            if response_msg.success:
546                return response_msg.read_data
547            logging.error("Failed to find memory region with id %d",
548                          self._mem_id)
549        return None
550
551    def updateBytes(self, data, length, start=0):
552        """This method performs actual write operation.
553
554        This method helps caller perform actual write operation on the
555        memory region, because host side won't be able to cast c++ pointers.
556
557        Args:
558            data: string, bytes to be written into memory.
559                  Caller can use bytearray() function to convert python
560                  data structures into python, and call str() on the resulting
561                  bytearray object.
562            length: int, number of bytes to write.
563            start: int, offset from the start of memory region to be modified.
564
565        Returns:
566            bool, true if the operation succeeds, false otherwise.
567        """
568        request_msg = self._createTemplateRequestMessage(
569            ResControlMsg.MEM_PROTO_UPDATE_BYTES)
570        request_msg.write_data = data
571        request_msg.start = start
572        request_msg.length = length
573
574        response_msg = self._client.SendHidlMemoryRequest(request_msg)
575        if response_msg is not None:
576            if not response_msg.success:
577                logging.error("Failed to find memory region with id %d",
578                              self._mem_id)
579            return response_msg.success
580        return False
581
582    def commit(self):
583        """Caller signals done with operating on the memory region.
584
585        Caller needs to call this method after reading/writing.
586
587        Returns:
588            bool, true if the operation succeeds, false otherwise.
589        """
590        request_msg = self._createTemplateRequestMessage(
591            ResControlMsg.MEM_PROTO_COMMIT)
592
593        response_msg = self._client.SendHidlMemoryRequest(request_msg)
594        if response_msg is not None:
595            if not response_msg.success:
596                logging.error("Failed to find memory region with id %d",
597                              self._mem_id)
598            return response_msg.success
599        return False
600
601    def getSize(self):
602        """Gets the size of the memory region.
603
604        Returns:
605            int, size of memory region, -1 to signal operation failure.
606        """
607        request_msg = self._createTemplateRequestMessage(
608            ResControlMsg.MEM_PROTO_GET_SIZE)
609
610        response_msg = self._client.SendHidlMemoryRequest(request_msg)
611        if response_msg is not None:
612            if response_msg.success:
613                return response_msg.mem_size
614            logging.error("Failed to find memory region with id %d",
615                          self._mem_id)
616        return -1
617
618    @property
619    def memId(self):
620        """Gets the id assigned from the target side.
621
622        Returns:
623            int, id of the memory object.
624        """
625        return self._mem_id
626
627    def _createTemplateRequestMessage(self, operation):
628        """Creates a template HidlMemoryRequestMessage.
629
630        This method creates a message that contains common arguments among
631        all hidl_memory operations.
632
633        Args:
634            operation: HidlMemoryOp, hidl_memory operations.
635                       (see test/vts/proto/VtsResourceControllerMessage.proto).
636
637        Returns:
638            HidlMemoryRequestMessage, hidl_memory request message.
639                (See test/vts/proto/VtsResourceControllerMessage.proto).
640        """
641        request_msg = ResControlMsg.HidlMemoryRequestMessage()
642        request_msg.operation = operation
643        request_msg.mem_id = self._mem_id
644        return request_msg
645
646
647class ResourceHidlHandleMirror(mirror_object.MirrorObject):
648    """This class mirrors hidl_handle resource allocated on the target side.
649
650    TODO: support more than file types in the future, e.g. socket, pipe.
651
652    Attributes:
653        _client: the TCP client instance.
654        _handle_id: int, used to identify the handle object on the target side.
655    """
656
657    def __init__(self, client, handle_id=-1):
658        super(ResourceHidlHandleMirror, self).__init__(client)
659        self._handle_id = handle_id
660
661    def CleanUp(self):
662        """Close open file descriptors on target-side drivers.
663
664        Developers can call this method to close open file descriptors
665        in all handle objects.
666        Note: This method needs to be called before self._client
667        is disconnected. self._client is most likely initialized in
668        one of the hal_mirror.
669        """
670        request_msg = self._createTemplateRequestMessage(
671            ResControlMsg.HANDLE_PROTO_DELETE)
672        self._client.SendHidlHandleRequest(request_msg)
673
674    def _createHandleForSingleFile(self, filepath, mode, int_data):
675        """Initiate a hidl_handle object containing a single file descriptor.
676
677        This method stores the handle_id in the class attribute.
678        Users should not directly call this method to create a new
679        handle object, because it will overwrite the original handle object,
680        making that handle object out of reference.
681        Users should always call InitHidlHandle() in mirror_tracker.py to get
682        a new handle object.
683
684        Args:
685            filepath: string, path to the file to be opened.
686            mode: string, specifying the mode to open the file.
687            int_data: int list, useful integers to store in the handle object.
688        """
689        # Prepare arguments.
690        request_msg = self._createTemplateRequestMessage(
691            ResControlMsg.HANDLE_PROTO_CREATE_FILE)
692        request_msg.handle_info.num_fds = 1
693        request_msg.handle_info.num_ints = len(int_data)
694
695        # TODO: support more than one file descriptors at once.
696        # Add the file information into proto message.
697        fd_message = request_msg.handle_info.fd_val.add()
698        fd_message.type = CompSpecMsg.FILE_TYPE
699        fd_message.file_mode_str = mode
700        fd_message.file_name = filepath
701
702        # Add the integers into proto message.
703        request_msg.handle_info.int_val.extend(int_data)
704
705        # Send and receive data.
706        response_msg = self._client.SendHidlHandleRequest(request_msg)
707        if response_msg is not None and response_msg.new_handle_id != -1:
708            self._handle_id = response_msg.new_handle_id
709        else:
710            logging.error("Failed to create handle object.")
711
712    def readFile(self, read_data_size, index=0):
713        """Reads from a given file in the handle object.
714
715        Args:
716            read_data_size: int, number of bytes to read.
717            index: int, index of file among all files in the handle object.
718                        Optional if host only wants to read from one file.
719
720        Returns:
721            string, data read from the file.
722        """
723        # Prepare arguments.
724        request_msg = self._createTemplateRequestMessage(
725            ResControlMsg.HANDLE_PROTO_READ_FILE)
726        request_msg.read_data_size = read_data_size
727
728        # Send and receive data.
729        response_msg = self._client.SendHidlHandleRequest(request_msg)
730        if response_msg is not None and response_msg.success:
731            return response_msg.read_data
732        # TODO: more detailed error message.
733        logging.error("Failed to read from the file.")
734        return None
735
736    def writeFile(self, write_data, index=0):
737        """Writes to a given file to the handle object.
738
739        Args:
740            write_data: string, data to be written into file.
741            index: int, index of file among all files in the handle object.
742                        Optional if host only wants to write into one file.
743
744        Returns:
745            int, number of bytes written.
746        """
747        # Prepare arguments.
748        request_msg = self._createTemplateRequestMessage(
749            ResControlMsg.HANDLE_PROTO_WRITE_FILE)
750        request_msg.write_data = write_data
751
752        # Send and receive data.
753        response_msg = self._client.SendHidlHandleRequest(request_msg)
754        if response_msg is not None and response_msg.success:
755            return response_msg.write_data_size
756        # TODO: more detailed error message.
757        logging.error("Failed to write into the file.")
758        return None
759
760    @property
761    def handleId(self):
762        """Gets the id assigned from the target side.
763
764        Returns:
765            int, id of the handle object.
766        """
767        return self._handle_id
768
769    def _createTemplateRequestMessage(self, operation):
770        """Creates a template HidlHandleRequestMessage.
771
772        This method creates a HidlHandleRequestMessage with common arguments
773        among all hidl_handle operations.
774
775        Args:
776            operation: HidlHandleOp, hidl_handle operations.
777                       (see test/vts/proto/VtsResourceControllerMessage.proto).
778
779        Returns:
780            HidlHandleRequestMessage, hidl_handle request message.
781                (See test/vts/proto/VtsResourceControllerMessage.proto).
782        """
783        request_msg = ResControlMsg.HidlHandleRequestMessage()
784        request_msg.operation = operation
785        request_msg.handle_id = self._handle_id
786        return request_msg
787